简体   繁体   中英

why doesn't this lock statement work?

class Program
{
    static object test = new object();
    static void Main(string[] args)
    {
        new Program().test2();
        Console.ReadKey();
    }

    public void test1()
    {
        lock (test)
        {
            Console.WriteLine("test1");
        }
    }

    public void test2()
    {
        lock (test)
        {
            test1();
            Console.WriteLine("test2");
        }
    }
}

Is the code above supposed to first finish statements in lock statement of test2() then go to the test1()? (ie Doesn't the output supposed to be like this? : test2 test1 )

No. The sequence of events (identation represents call stack or logical operations) is:

  • Main calls test2
    • test2 attempts to acquire the monitor associated with the test object (start of the lock statement)
      • Monitor is currently unowned. Success!
      • The current thread now "owns" that monitor, with a count of 1
    • test2 calls test1
      • test1 attempts to acquire the monitor for the test object (start of the lock statement)
        • Monitor is currently owned... but by the current thread. Success!
        • The current thread still "owns" the monitor, with a count of 2
      • test1 prints "test1"
      • test1 releases the monitor (end of the lock statement)
        • The current thread still "owns" the monitor, with a count of 1
      • test1 returns
    • test2 prints "test2"
    • test2 releases the monitor (end of the lock statement)
      • The monitor is now unowned (so another thread could acquire it)
    • test2 returns

It's important to note that monitors are re-entrant - if the current thread already owns the monitor, then another attempt to acquire it will just increase the count, rather than blocking.

If monitors weren't re-entrant, the output wouldn't be "test2, test1" - it would just be deadlock.

A monitor is re-entrant on the same thread. Pretty important to avoid accidental dead-lock, your code would freeze solidly if it didn't have that behavior.

A Mutex is also re-entrant, a Semaphore is not.

The implementation is pretty straight forward. A Monitor stores two pieces of information. The owner Thread.ManagedId of the thread that entered it and a counter that counts the number of times it was entered. So the first lock can enter since it isn't owned, it sets the owner to your thread and the count to 1. The second lock is allowed to enter since the thread ID matches, the count increments to 2. At the end of the second lock, the count decrements to 1 again. At the end of the first, the count decrements to 0 and that resets the owner.

Locking isn't supposed to be used in single-threaded scenarios. It's purpose is to be used in cross threads calls to the same methods or object instances.

The behavior you're noticing is normal.

You will typically want to synchronize access to resources (variables, collections, etc) when two or more threads can access them at the same time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM