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

C# 无锁多线程适用于真正的线程专家,c#,.net,multithreading,lock-free,C#,.net,Multithreading,Lock Free,我读了一篇关于一个问题的文章,他在文章中提到: 就我而言,无锁多线程是为真正的线程专家设计的,我不是其中之一 这不是我第一次听到这句话,但是如果你对学习如何编写无锁多线程代码感兴趣的话,我发现很少有人谈论你是如何做到这一点的 所以我的问题是,除了学习所有关于线程的知识外,你从哪里开始学习编写无锁多线程代码,还有哪些好的资源 干杯当谈到多线程时,您必须确切知道自己在做什么。我的意思是探索在多线程环境中工作时可能发生的所有场景/情况。无锁多线程不是我们合并的库或类,它是我们在线程旅程中获得的知识/经

我读了一篇关于一个问题的文章,他在文章中提到:

就我而言,无锁多线程是为真正的线程专家设计的,我不是其中之一

这不是我第一次听到这句话,但是如果你对学习如何编写无锁多线程代码感兴趣的话,我发现很少有人谈论你是如何做到这一点的

所以我的问题是,除了学习所有关于线程的知识外,你从哪里开始学习编写无锁多线程代码,还有哪些好的资源


干杯

当谈到多线程时,您必须确切知道自己在做什么。我的意思是探索在多线程环境中工作时可能发生的所有场景/情况。无锁多线程不是我们合并的库或类,它是我们在线程旅程中获得的知识/经验。

Google for and

在这一点上,我同意约翰·斯基特的观点;无锁线程是魔鬼的游乐场,最好留给那些知道自己需要知道什么的人去做。

现在没有“无锁线程”这样的东西。在上个世纪末,计算机硬件速度慢且价格昂贵的时候,它是学术界和类似机构的一个有趣的游乐场。这一直是我最喜欢的,现代的硬件已经把它变成了牧场。它不再工作了

两项发展结束了这一局面:RAM和CPU速度之间的差距越来越大。以及芯片制造商在一个芯片上安装多个CPU核的能力

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

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

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

这在.NET中可用,Thread.MemoryBarrier()方法实现了一个。考虑到这是lock语句所做的90%的工作(以及95%以上的执行时间),避免使用.NET提供的工具并尝试实现自己的工具,您根本就没有领先。

Joe Duffy的书:

他还就这些话题写了一篇博客

正确使用低锁程序的诀窍是从深层次准确地理解内存模型的规则在硬件、操作系统和运行时环境的特定组合上是什么

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

当前的“无锁”实现大多数时候遵循相同的模式:

  • 阅读一些状态并复制它*
  • 修改副本*
  • 进行联锁操作
  • 如果失败,请重试
(*可选:取决于数据结构/算法)

最后一个位与旋转锁极为相似。事实上,这是一个基本原则。:)
我同意@nobugz的观点:在无锁多线程中使用的联锁操作的成本是很高的

然而,使用“无锁”的数据结构所获得的好处是,您的“锁”是非常细粒度的。这降低了两个并发线程访问同一个“锁”(内存位置)的可能性

大多数情况下,诀窍在于您没有专用锁,而是将数组中的所有元素或链接列表中的所有节点视为“旋转锁”。如果自上次读取后没有更新,则读取、修改并尝试更新。如果有,请重试。
这使得您的“锁定”(哦,对不起,非锁定:)非常细粒度,而不会引入额外的内存或资源需求。
使其更细粒度可以降低等待的概率。在不引入额外资源需求的情况下尽可能地使其细粒度听起来很棒,不是吗

然而,大部分的乐趣都来自于此。
与人们的直觉相反,CPU可以自由地对内存读/写进行重新排序——顺便说一句,它们非常聪明:你很难从一个线程观察到这一点。然而,当您开始在多核上执行多线程时,您将遇到问题。你的直觉会崩溃:仅仅因为指令在你的代码中出现得更早,并不意味着它实际上会发生得更早。CPU可以无序地处理指令:它们特别喜欢对指令进行处理