什么是常见的并发陷阱?

31 concurrency multithreading design-patterns

我正在研究如何让我们的团队了解并发性.开发人员陷入周围并发的最常见陷阱是什么?例如,在.Net中,关键字static为许多并发问题打开了大门.

是否有其他设计模式不是线程安全的?

更新

这里有很多很棒的答案很难选择一个作为公认的答案.请务必滚动浏览所有提示.

Die*_*nte 17

这个帖子已经有很多很好的答案和指针,但是我要补充一点.

不要依赖测试来查找比赛条件和死锁

假设您拥有所有良好的开发流程:每个组件的单元测试,每个夜间构建的冒烟测试,要求每个开发人员的更改在签入之前通过测试等.

所有这一切都很好,但它会导致一种态度"好吧,它通过了测试套件,因此它不能成为我代码中的错误." 在并发编程中不能很好地为你服务.实时并发错误非常难以重现.在失败之前,您可以运行一段具有竞争条件十亿次的代码.

您将不得不调整您的流程,以更加重视由您的最佳思想进行的代码审查.仅针对并发问题进行单独的代码审查并不是一个坏主意.

您将不得不更加重视使应用程序自我调试.也就是说,当您在测试实验室或客户的站点出现故障时,您需要确保捕获并记录足够的信息以便进行明确的事后检查,因为您能够在以下情况下重现错误报告的可能性您的便利性可以忽略不计.

您将不得不更加强调代码中的偏执健全性检查,以便尽可能地检测到故障,而不是50,000行代码.

偏执狂.非常偏执.


Ser*_*gel 15

一种是竞争条件,它基本上假设一条代码将在另一条并发代码之前/之后运行.

还有死锁,即代码A等待代码B释放资源Y,而代码B等待A释放资源X.


vy3*_*y32 12

我和朋友和同事一起教并发很多.以下是一些重大缺陷:

  • 假设一个主要在许多线程中读取并且只写在一个线程中的变量不需要被锁定.(在Java中,这种情况可能导致读取线程永远不会看到新值.)
  • 假设线程将以特定顺序运行.
  • 假设线程将同时运行.
  • 假设线程不会同时运行.
  • 假设所有线程都会在任何一个线程结束之前前进.

我也看到:

  • thread_fork()和之间的大混乱fork().
  • 在一个线程中分配内存而free()在另一个线程中分配d 时的混淆.
  • 由于某些库是线程安全而某些库不是线程安全而导致的混淆.
  • 人们使用自旋锁时应该使用睡眠和唤醒,或者选择,或者语言支持的任何阻止机制.


Mau*_*gan 11

并发没有很多陷阱.

但是,同步对共享数据的访问非常棘手.

以下是编写共享数据同步代码的人应该能够回答的一些问题:

  1. 什么是InterlockedIncrement?
  2. 为什么InterlockedIncrement需要以汇编语言级别存在?
  3. 什么是读写重新排序?
  4. 什么是volatile关键字(在c ++中)以及何时需要使用它?
  5. 什么是同步层次结构?
  6. 什么是ABA问题?
  7. 什么是缓存一致性?
  8. 什么是记忆障碍?

"共享一切"并发性是一种极其漏洞的抽象.改为采用无共享消息传递.

  • -1此清单仅与MS-Windows上的C++(或C?)编程有关. (4认同)

Jam*_*hon 10

最大的缺陷之一是首先使用并发.并发性增加了大量的设计/调试开销,因此您必须检查问题并查看它是否真的需要并发.

并发在某些领域肯定是不可避免的,但是当它可以避免时,请避免它.


Dun*_*unk 5

要记住的一个事实是,即使最初的开发人员让他们的任务模型正常工作(这是一个很大的假设),那么随后的维护团队肯定会以难以想象的方式把事情搞砸。这样做的目的是限制整个系统中的并发痕迹。尽你最大的努力确保你的大部分系统都幸福地不知道并发正在发生。这减少了不熟悉任务分配模型的人无意中搞砸的机会。

人们经常让线程/任务变得疯狂。一切都在自己的线程上运行。最终结果是几乎每段代码都必须密切关注线程问题。它迫使其他简单的代码充斥着锁定和同步混乱。每次我看到这个,系统最终都会变成一个无法维护的烂摊子。然而,每次我看到这个,最初的开发者仍然坚持这是一个很好的设计:(

就像多重继承一样,如果你想创建一个新的线程/任务,那么假设你错了,直到证明不是这样。我什至无法计算我看到线程 A 调用线程 B 然后线程 B 调用线程 C 然后线程 C 调用 D 模式都在等待来自前一个线程的响应的次数。所有代码所做的就是通过不同的线程进行冗长的函数调用。当函数调用工作正常时不要使用线程。

永远记住,当您想要并发工作时,消息队列是您最好的朋友。

我发现创建一个处理几乎所有并发问题的核心基础架构效果最好。如果核心基础架构之外的任何线程必须与另一个软件进行通信,那么它们必须通过核心基础架构。这样,系统的其余部分可以保持并发不知道,并发问题可以由希望了解并发的人来处理。