【问题标题】:locking only when modifying vs entire method仅在修改 vs 整个方法时锁定
【发布时间】:2010-12-14 16:34:15
【问题描述】:

什么时候应该使用锁?仅在修改数据或访问数据时?

public class Test {
    static Dictionary<string, object> someList = new Dictionary<string, object>();

    static object syncLock = new object();

    public static object GetValue(string name) {
        if (someList.ContainsKey(name)) {
            return someList[name];
        } else {
            lock(syncLock) {
                object someValue = GetValueFromSomeWhere(name);
                someList.Add(name, someValue);
            }
        }
    }
}

是否应该在整个块周围加一个锁,还是只将它添加到实际修改中就可以了?我的理解是,仍然可能存在一些竞争条件,其中一个呼叫可能没有找到它并开始添加它,而紧随其后的另一个呼叫也可能遇到相同的情况 - 但我不确定。锁定仍然如此混乱。我没有遇到上述类似代码的任何问题,但到目前为止我很幸运。上面的任何帮助以及如何/何时锁定对象的任何好的资源都会被应用。

【问题讨论】:

    标签: c# multithreading


    【解决方案1】:

    读取时也必须加锁,否则会得到不可靠的数据,甚至在并发修改物理改变目标数据结构时出现异常。

    在上述情况下,您需要确保多个线程不会同时尝试添加该值,因此您至少需要一个读锁,同时检查它是否已经存在。否则多个线程可能决定添加,发现值不存在(因为这个检查没有锁定),然后都尝试依次添加(获得锁后)

    如果您有很多读取并且只有少量写入,您可以使用ReaderWriterLockSlim。在上面的代码中,一旦您决定需要添加它,您将获取读锁以进行检查并升级为写锁。在大多数情况下,只需要一个读锁(它允许您的阅读器线程仍然并行运行)。

    有可用的 .Net 4 锁定原语here 的摘要。在深入研究多线程代码之前,您绝对应该了解这一点。选择正确的锁定机制可以产生巨大的性能差异。

    你说得对,到目前为止你很幸运——这是并发错误的常见特征。如果没有针对性的负载测试,它们通常很难重现,这意味着正确的设计(当然还有详尽的测试)对于避免令人尴尬和令人困惑的生产错误至关重要。

    【讨论】:

    • 所有可用锁定机制的绝佳链接。我只听说过lock
    • 祝你好运 - 如果你能掌握这些东西,你将成为为增加多核开发做好充分准备的少数开发者。
    【解决方案2】:

    在检查name 是否存在之前锁定整个块。否则,理论上,另一个线程可以在检查和添加它的代码之间添加它。

    实际上,当您执行 Add 时锁定实际上并没有做任何事情。所做的只是防止另一个线程同时添加一些东西。但是,由于其他线程已经决定要执行添加操作,因此只要释放锁,它就会尝试执行此操作。

    【讨论】:

    • 另一个进程?术语很重要。
    【解决方案3】:

    如果一个资源只能被多个线程访问,则不需要任何锁。

    如果一个资源可以被多个线程访问并且可以修改,那么所有的访问/修改都需要同步。在您的示例中,如果GetValueFromSomeWhere 需要很长时间才能返回,则可以使用name 中的相同值进行第二次调用,但该值尚未存储在Dictionary 中。

    【讨论】:

      【解决方案4】:

      ReaderWriterLock 或 4.0 以下的 slim 版本。

      您将为读取获取读取器锁(将允许并发读取)并在写入时将锁升级为写入器锁(一次只允许一次写入,并将阻止所有读取,直到完成,以及并发写入线程)。

      确保使用模式释放锁以避免死锁:

                  void Write(object[] args) 
                  {
      
      
                         this.ReaderWriterLock.AquireWriteLock(TimeOut.Infinite);
      
                          try 
                          {
                            this.myData.Write(args);
      
                          }
                          catch(Exception ex) 
                          {
      
                          }
                          finally 
                          {
      
                              this.ReaderWriterLock.RelaseWriterLock();
                          }
      
                  }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-17
        • 1970-01-01
        • 2023-04-11
        • 1970-01-01
        相关资源
        最近更新 更多