第二个线程是否可以进入同一个关键部分,因为第一个线程使用相同的同步锁定名为Monitor.Wait?

时间:2016-05-23 15:16:32

标签: c# .net multithreading

如果我想的话,请告诉我。

  1. 其他线程无法使用相同的关键部分 同一个锁只是因为第一个线程叫Monitor.Wait,对吗? Wait方法仅允许获取不同的线程 相同的监视器,即相同的同步锁,但仅用于不同的关键部分,从不用于相同的关键部分 节

    我的理解是否正确?

    因为如果Wait方法意味着任何人现在都可以输入 使用同一个锁的同一个关键部分,然后就会失败 同步的整个目的,对吧?

    所以,在下面的代码中(写在记事本中,所以请原谅任何 错别字),ThreadProc2只能使用syncLock输入代码 ThreadProc2而不是前一个帖子中的ThreadProc1 持有并随后放弃锁定正在执行 ThreadProc1,对吧?

  2. 两个或多个线程可以使用相同的同步锁来运行 不同的代码片段在同一时间,对吧?同样的问题 以上,基本上,只是为了对称而确认 第3点。

  3. 两个或多个线程可以使用不同的同步锁 运行相同的代码,即进入相同的关键部分。

  4. 用于更正格式的Boilerplate文本。

    class Foo
    {
        private static object syncLock = new object();
    
        public void ThreadProc1()
        {
            try
            {
                Monitor.Enter(syncLock);
    
                Monitor.Wait(syncLock);
    
                Thread.Sleep(1000);
            }
            finally
            {
                if (Monitor.IsLocked(syncLock))
                {
                    Monitor.Exit(syncLock);
                }
            }
        }
    
        public void ThreadProc2()
        {
            bool acquired = false;
    
            try
            {
                // Calling TryEnter instead of
                // Enter just for the sake of variety
                Monitor.TryEnter(syncLock, ref acquired);
    
                if (acquired)
                {
                    Thread.Sleep(200);
                    Monitor.Pulse(syncLock);
                }
            }
            finally
            {
                if (acquired)
                {
                    Monitor.Exit(syncLock);
                }
            }
        }
    }
    

    更新

    以下插图证实了#3是正确的,尽管我认为这不是一件好事。

    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    
    namespace DifferentSyncLockSameCriticalSection
    {
        class Program
        {
            static void Main(string[] args)
            {
                var sathyaish = new Person { Name = "Sathyaish Chakravarthy" };
                var superman = new Person { Name = "Superman" };
                var tasks = new List<Task>();
    
                // Must not lock on string so I am using
                // an object of the Person class as a lock
                tasks.Add(Task.Run( () => { Proc1(sathyaish); } ));
                tasks.Add(Task.Run(() => { Proc1(superman); }));
    
                Task.WhenAll(tasks);
    
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
    
            static void Proc1(object state)
            {
                // Although this would be a very bad practice
                lock(state)
                {
                    try
                    {
                        Console.WriteLine((state.ToString()).Length);
                    }
                    catch(Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                    }
                }
            }
        }
    
        class Person
        {
            public string Name { get; set; }
    
            public override string ToString()
            {
                return Name;
            }
        }
    }
    

1 个答案:

答案 0 :(得分:4)

当一个线程调用Monitor.Wait时,它被挂起并释放锁。这将允许另一个线程获取锁,更新某个状态,然后调用Monitor.Pulse以便与其他线程通信已发生的事情。您必须已获得锁定才能拨打Pulse。在Monitor.Wait返回之前,框架将重新获取调用Wait

的线程的锁

为了使两个线程相互通信,它们需要使用相同的同步原语。在您的示例中,您使用了监视器,但通常需要将此与Wait为响应Pulse而返回的某种测试结合使用。这是因为即使Wait没有被调用,Pulse在技术上也可以返回(尽管在实践中这不会发生)。

还值得记住,对Pulse的调用不是“粘性”,因此如果没有人在监视器上等待,那么Pulse什么都不做,随后对Wait的调用将会想念Pulse被召唤的事实。这是为什么你倾向于在调用Pulse之前记录已完成某事的事实的另一个原因(参见下面的例子)。

两个不同的线程使用相同的锁来运行不同的代码完全有效 - 实际上这是典型的用例。例如,一个线程获取锁以写入一些数据,另一个线程获取锁以读取数据。但是,重要的是要意识到它们不会同时运行。获取锁的行为会阻止另一个线程获取相同的锁,因此任何尝试获取锁的线程都将被锁定,直到另一个线程释放锁。

在第3点,你问:

  

两个或多个线程可以使用不同的同步锁来运行   相同的代码,即进入相同的关键部分。

但是,如果两个线程使用不同的锁,则它们不会进入相同的关键部分。关键部分由保护它的锁表示 - 如果它们是不同的锁,则它们是碰巧访问该部分内的一些公共数据的不同部分。您应该避免这样做,因为它可能导致一些难以调试的数据竞争条件。

对于您要完成的任务,您的代码有点过于复杂。例如,假设我们有2个线程,当有可供另一个处理的数据时,一个线程会发出信号:

    class Foo
    {
        private readonly object syncLock = new object();
        private bool dataAvailable = false;

        public void ThreadProc1()
        {
            lock(syncLock)
            {
                while(!dataAvailable)
                {
                    // Release the lock and suspend
                    Monitor.Wait(syncLock);
                }

                // Now process the data
            }
        }

        public void ThreadProc2()
        {
            LoadData();

            lock(syncLock)
            {
                dataAvailable = true;
                Monitor.Pulse(syncLock);
            }
        }

        private void LoadData()
        {
            // Gets some data
        }
    }
}