无锁多线程适用于真正的线程专家

时间:2010-03-27 10:50:58

标签: c# .net multithreading lock-free

我正在阅读answer Jon Skeet提出问题的内容,并在其中提到了这一点:

  

就我而言,无锁多线程适用于真正的线程专家,其中我不是一个。

这不是我第一次听到这个,但是如果你有兴趣学习如何编写无锁多线程代码,我发现很少有人在谈论你如何实际做到这一点。

所以我的问题是除了学习有关线程的所有内容之外,等等你在哪里开始尝试学习专门编写无锁多线程代码以及什么是好资源。

干杯

6 个答案:

答案 0 :(得分:95)

答案 1 :(得分:27)

Joe Duffy的书:

http://www.bluebytesoftware.com/books/winconc/winconc_book_resources.html

他还撰写了一篇关于这些主题的博客。

让低锁定程序正确的诀窍是深入了解正确内存模型的规则在您的硬件,操作系统和运行时环境的特定组合上的含义。 / p>

我个人并不聪明到能够在InterlockedIncrement之外进行正确的低锁编程,但如果你是,那很好,那就去吧。只要确保你在代码中留下了大量的文档,这样那些不那么聪明的人就不会意外地破坏你的一个内存模型不变量并引入一个不可能找到的bug。

答案 2 :(得分:19)

这些天没有“无锁线程”这样的东西。对于学术界等而言,这是一个有趣的游乐场,早在上个世纪末,计算机硬件变得缓慢且昂贵。 Dekker's algorithm总是我最喜欢的,现代硬件已经把它放到了牧场。它不再起作用了。

两个发展已经结束了这一点:RAM的速度和CPU之间的差距越来越大。并且芯片制造商能够在芯片上放置多个CPU内核。

RAM速度问题要求芯片设计人员在CPU芯片上放置缓冲器。缓冲区存储代码和数据,可由CPU内核快速访问。并且可以以低得多的速率从RAM读取和写入RAM。此缓冲区称为CPU缓存,大多数CPU至少有两个。第一级缓存小而快,第二级缓存大而慢。只要CPU可以从第一级缓存读取数据和指令,它就会快速运行。高速缓存未命中非常昂贵,如果数据不在第一个高速缓存中,它会使CPU休眠多达10个周期,如果它不在第二个高速缓存中则多达200个周期,并且需要从RAM。

每个CPU核心都有自己的缓存,它们存储自己的RAM“视图”。当CPU写入数据时,写入缓存,然后缓慢地刷新到RAM。不可避免的是,每个核心现在都有不同的RAM内容视图。换句话说,在RAM写周期完成之前,一个CPU不知道另一个CPU写了什么 CPU刷新了自己的视图。

这与线程非常不兼容。当你必须读取另一个线程写入的数据时,你总是真的关心另一个线程的状态。为了确保这一点,您需要显式编程所谓的内存屏障。它是一个低级CPU原语,可确保所有CPU缓存处于一致状态并具有RAM的最新视图。所有挂起的写入都必须刷新到RAM,然后需要刷新缓存。

这在.NET中可用,Thread.MemoryBarrier()方法实现了一个。鉴于这是锁定语句所执行作业的90%(以及执行时间的95%以上),您只是避免使用.NET提供的工具并尝试实现自己的工具。

答案 3 :(得分:6)

Google lock free data structuressoftware transactional memory

我同意John Skeet对此的看法;无锁线程是魔鬼的游乐场,最让人知道他们知道他们需要知道什么。

答案 4 :(得分:0)

说到多线程,您必须确切地知道自己在做什么。我的意思是探索在多线程环境中工作时可能发生的所有可能情况/案例。无锁多线程不是我们合并的图书馆或课程,而是我们在线程旅程中获得的知识/经验。

答案 5 :(得分:0)

即使在.NET中使用无锁线程也很困难,通常你可以通过准确研究需要锁定的内容并最小化锁定部分来使用锁定时进行重大改进...这也称为最小化锁定粒度

举个例子,假设您需要使收集线程安全。如果它对每个项目执行一些CPU密集型任务,那么不要盲目地在迭代集合的方法周围抛出一个锁。您可能只需要锁定创建集合的浅表副本。然后迭代副本可以在没有锁定的情况下工作。当然,这在很大程度上取决于您的代码的细节,但我已经能够用这种方法解决lock convoy问题。