我读到有关锁的信息,尽管一点也不了解。
我的问题是,为什么我们使用未使用的object并将其锁定,以及这如何使某些线程安全或如何在多线程中发挥作用?没有其他方法可以创建线程安全的代码。

public class test {
    private object Lock { get; set; }

    ...
    lock (this.Lock) { ... }
    ...
}

抱歉,我的问题很愚蠢,但是我听不懂,尽管我已经使用了很多次。

最佳答案

当一个线程正在修改另一个线程时从一个线程访问它的数据称为“数据竞争条件”(或简称为“数据竞争”),并且可能导致数据损坏。 (*)

锁只是避免数据争用的一种机制。如果两个(或多个)并发线程锁定了同一个锁定对象的,则在锁定期间,它们不再并发并且不再导致数据争用。本质上,我们是序列化对共享数据的访问。

诀窍是将锁保持为“宽”,因为您必须以避免数据争用,而则保持为“窄”,因为您可以通过并发执行来获得性能。这是一个很好的平衡点,可以很容易地从两个方向突破,这就是为什么多线程编程很难实现的原因。

一些准则:

  • 只要所有线程都在读取数据,并且没有人会修改它,就不需要进行锁定。
  • 相反,如果至少一个线程可能在某个时刻修改数据,则访问同一数据的所有并发代码路径必须通过锁正确地序列化,即使那些仅读取数据的锁也是如此。
  • 在一个代码路径中使用锁而不在另一个代码路径中使用锁将使数据在竞争条件下保持开放状态。
  • 同样,在一个代码路径中使用一个锁定对象,而在另一(并发)代码路径中使用另一个锁定对象,则不会序列化这些代码路径,从而使您对数据竞争敞开了大门。
  • 另一方面,如果两个并发代码路径访问不同的数据,则它们可以使用不同的锁对象。但是,只要有一个以上的锁定对象,请当心deadlocks。死锁通常也是“代码竞争条件”(还有heisenbug,请参见下文)。
  • 锁定对象不必(通常不是)与您要保护的数据相同的东西。不幸的是,没有语言工具可以让您“声明”哪些数据受哪个锁定对象保护,因此您必须非常仔细地为可能维护您代码的其他人和您自己记录“锁定约定”(因为即使经过很短的时间,您也会忘记锁定约定的一些细节和缺点。
  • 通常,尽可能多地保护锁对象不受外界干扰是一个好主意。毕竟,您将其用于非常敏感的锁定任务,并且您不希望外部参与者以无法预料的方式锁定它。这就是为什么使用this或公共(public)字段作为锁定对象通常是一个坏主意的原因。
  • lock关键字只是Monitor.EnterMonitor.Exit的一种更方便的语法。
  • 锁定对象可以是.NET中的任何对象,但是在对Monitor.Enter的调用中,value objects将是框内的,这意味着线程将不会共享同一锁定对象,从而使数据不 protected 。因此,仅将引用类型用作锁定对象。
  • 对于进程间通信,您可以使用全局互斥锁,可以通过将非空name传递给Mutex Constructor来创建。全局互斥锁提供的功能与常规“本地”锁定基本相同,不同之处在于它们可以在单独的进程之间共享。
  • 除了锁以外,还有其他同步机制,例如信号量,条件变量,消息队列或atomic operations。混合使用不同的同步机制时要小心。
  • 锁的行为也类似于memory barriers,它在现代多核,多缓存CPU上越来越重要。这是您需要锁定读取数据而不仅仅是写入的原因的一部分。

  • (*)之所以称为“竞赛”,是因为并发线程正在“竞赛”走向对共享数据执行操作,而赢得比赛的人将决定操作的结果。因此,结果取决于执行的时序,这在现代抢先式多任务OS上基本上是随机的。更糟糕的是,通过使用诸如调试器之类的工具来观察程序执行的简单 Action ,就很容易修改定时,这使它们变成"heisenbugs"(即,仅通过观察 Action 就可以改变所观察到的现象)。

    关于c# - 多线程概念和C#中的锁定,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10152613/

    10-16 10:17