【问题标题】:C# threading and synchronizationC# 线程和同步
【发布时间】:2011-06-15 17:37:43
【问题描述】:

我有一个用于同步(锁定)的私有静态字段。现在我有两个不想同时执行的函数。所以我这样做了:

public class Synchronization
{
    private static object _lock = new object();

    public void MethodA()
    {
        lock (_lock)
        {
            Console.WriteLine("I shouldn't execute with MethodB");
        }
    }

    public void MethodB()
    {
        lock (_lock)
        {
            Console.WriteLine("I shouldn't execute with MethodA");
        }
    }
}

我知道锁定一个对象会阻止单个函数的并行执行,但是如果我在同时运行的不同方法中使用相同的锁定对象,同样的工作吗?简单地说,任何其他线程都可以在另一个函数中获取已锁定对象的锁定吗?

【问题讨论】:

  • 何不自己尝试一下呢?
  • 另请注意,通过这种方式,您已经同步了该类的每个实例。如果您不希望这样,请删除锁定对象的“静态”部分。
  • @Andreas 我说的是理论实践。验证 MT 代码也非常困难。所以你的建议很幼稚。
  • @Andreas “自己试一试”并没有提供关于如何验证他的断言的 OP 详细信息。如果 OP 难以弄清楚他正在做的事情的语义,那么您应该能够看到您的建议存在的明显问题。这是一个扔掉的评论。
  • @Andreas Sure:“互联网永远不会忘记”。留在这里的任何问题、答案和 cmets 应该对站点的其他用户有用,并且最好为搜索引擎提供很好的汁液。 OP 询问您如何实际跟进您的评论,即“我该如何测试这个?- Tux”

标签: c# multithreading synchronization locking


【解决方案1】:

我相信您阅读 MSDN 上的 Lock Statement 是正确的。

【讨论】:

    【解决方案2】:

    你做得对。您创建了两个不会同时进入的关键部分。

    所以 MethodA 和 MethodB 不会同时“活跃”。并且同时只有一个 MethodA(和 MethodB)处于活动状态。

    这适用于您创建的所有对象。我的意思是:任何对象的任何 MethodA 或 MethodB 中只有一个线程。如果您希望锁定只发生在一个对象内,您可以使 _lock 对象不是静态的。

    【讨论】:

      【解决方案3】:

      一次只有一个线程可以获取锁,所以这个状态对于一个锁实例上的所有线程是独占的。因此,在您的示例中,对于 Synchronization 类的所有实例,在任何给定时间只能执行一个方法体,因为您的锁是静态的。如果您想锁定类的每个实例,请不要将锁定对象标记为静态。

      您对同步的假设是正确的。

      请注意,您应该标记锁定对象readonly,以获得完全防水的解决方案。就代码而言,锁定对象可能会被重新分配,从而破坏锁定语义,例如:

      public class Synchronization
      {
          private static object _lock = new object();
      
          public void MethodA()
          {
              lock (_lock)
              {
                  Console.WriteLine("I shouldn't execute with MethodB");
              }
          }
      
          public void MethodB()
          {
              //This shouldn't be allowed!
              _lock = new object();
      
              lock (_lock)
              {
                  Console.WriteLine("I shouldn't execute with MethodA");
              }
          }
      }
      

      锁定对象应标记为readonly,即:

      private static readonly object _lock = new object();
      

      【讨论】:

      • 你应该用of any given instance增强only one method body,因为你正在处理一个静态锁:)
      • @Andreas 我已经详细说明了静态锁的含义。
      【解决方案4】:

      根据作为锁定目标的对象授予锁定,而不是根据lock 语句发生的方法。因此,在您的情况下,您的多个线程可能会输入各种方法,但一次只有一个线程能够执行 lock 语句中的任何代码。

      【讨论】:

        【解决方案5】:

        首先,_lock 不应该是静态的。或者您是否希望对象的多个实例相互锁定?其次,你应该在一个类中只有一个同步方法。更重要的是,您应该避免类中同步方法之间的依赖关系。否则,您的方法的调用者可能会出错并出现意外行为。

        例如,考虑以下代码:

        class Synchronized
        {
            object lockObj = new object();
            int counter = 100;
        
            public void Decrement()
            {
                lock (this.lockObj)
                {
                    this.counter--;
                }
            }
        
            public int IsZero()
            {
                lock (this.lockObj)
                {
                    return this.counter == 0;
                }
            }
        }
        

        现在将如何处理共享同步实例?

        这样使用

        while (!synchronized.IsZero())
        {
            synchronized.Decrement();
        }
        

        现在线程 1 调用 Decrement,计数器变为 0,然后线程 2 立即调用 Decrement,因为它在 Decrement 方法中等待锁定,而不是 IsZero 方法。计数器现在是 -1 并且循环是无限的。

        不是锁定机制编程错误,而是调用者没有很好地使用它。如果你只在你的 Synchronized 类上暴露了一个同步方法,你就不会欺骗程序员盲目地相信它是安全的。

        应该是这样的:

        class Synchronized
        {
            object lockObj = new object();
            int counter = 100;
        
            public bool IfNotZeroDecrement()
            {
                lock (this.lockObj)
                {
                    if (this.counter > 0)
                        this.counter--;
        
                    return this.counter > 0;
                }
            }    
        }
        
        /// Usage:
        while (synchronized.IfZeroDecrement())
        {
        }
        

        【讨论】:

        • 我不同意类中的一种同步方法。您的示例只是实施不佳。如果你不能减少小于 0 很重要,你的减量应该处理这个问题。
        • @btlog 嗯,在这个简单的例子中很明显,但在更复杂的场景中,它就不会那么明显了。您不应该将所有责任都推给代码的用户,否则您以后可能会遇到难以调试的严重麻烦。做到安全和清洁,将来每个人都会从中受益。
        • 关于这个(和其他)的推荐阅读:blog.objectmentor.com/articles/2008/04/08/clean-code-whew“方法之间的依赖关系可以破坏并发代码”
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-08
        • 2014-01-26
        • 1970-01-01
        • 2011-12-28
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多