在C++中深入研究多线程编程时,有哪些"需要注意的事项"

Mar*_*ark 54 c++ multithreading

我目前正在开发一个使用C++的无线网络应用程序,它正处于一个我想要在一个进程中使用多线程软件而不是将它们全部放在不同进程中的程度.从理论上讲,我理解多线程,但我还没有实际潜入.

在C++中编写多线程代码时,每个程序员应该知道什么?

Ari*_*iel 24

我将重点放在设计尽可能多的分区上,这样你就可以跨线程分享最少量的共享内容.如果你确定你没有在线程之间共享静态和其他资源(除了那些你将用进程而不是线程设计它的共享),你会没事的.

因此,虽然是的,你必须考虑锁,信号量等概念,解决这个问题的最佳方法是尽量避免它们.

  • `volatile`与多线程无关.该关键字用于内存映射硬件 - 确保在写入映射到某个硬件设备的内存位置时,立即执行写入,而不是最初缓存在寄存器中.但这并不能保证写入是*atomic*,这是在多线程上下文中安全使用它的必要条件.通常,`volatile`对于线程无用.你应该使用记忆障碍.或者确保您的静态资源是不可变的. (15认同)
  • 鉴于这个问题是多线程编程的初学者,我认为应该完全避免volatile.即使适合某些情况(我有疑问),初学者也无法分辨这些情况何时发生 (4认同)
  • @jalf:`volatile`可以解释为意味着值可能在外部发生变化,这在多线程上下文中非常有用.当然,如果它是原子的,它会更有用. (3认同)
  • 当你想在一个其他线程写入的值的循环中进行快速n-dirty int/bool检查时,`volatile`有时是可以接受的,而你不关心时间或顺序.那么就不需要互斥. (3认同)

Ara*_*raK 20

我在这个问题上根本不是专家.只是一些经验法则:

1)为简单起见,即使在最简单的例子中,在并发代码中也很难找到错误.
2)C++为您提供了一个非常优雅的范例来管理资源(互斥,信号量......):RAII.我发现使用它boost::thread比使用POSIX线程更容易.
3)将代码构建为线程安全的.如果你不这样做,你的程序可能会表现得很奇怪.

  • @Mark:主要的优点是可移植性和C++接口 - 特别是用于管理互斥锁等内容的RAII类. (10认同)
  • Boost线程是UNIX系统上POSIX线程的包装器,也是Windows系统上的Win32线程的包装器.Boost线程是一个C++库,易于在C++应用程序中使用,其中POSIX Threads是一个C库,需要大量工作才能与您的对象一起使用. (6认同)
  • 使用boost :: thread的另一个好处是它是进入新C++标准的线程代码的基础...所以使用boost :: thread编写的代码将表现得与新标准中的线程代码具有相同的构造.我也同意其他人所说的优点......我强烈推荐使用boost :: thread进行多线程处理. (2认同)

gas*_*ard 14

我正是在这种情况下:我写了一个带有全局锁的库(许多线程,但在库中一次只运行一个)并且我正在重构它以支持并发.

我已经阅读过有关该主题的书籍,但我所学到的内容有以下几点:

  1. 想想并行:想象一下人群通过代码.在已经执行的情况下调用方法时会发生什么?
  2. 思考共享:想象很多人试图同时阅读和改变共享资源.
  3. 设计:避免第1点和第2点可以引发的问题.
  4. 从不认为你可以忽视边缘情况,他们会很难咬你.

由于您无法对并发设计进行校对测试(因为线程执行交错不可重现),您必须通过仔细分析代码路径并记录代码的使用方式来确保您的设计是健壮的.

一旦了解了应该如何以及在何处瓶颈代码,您就可以阅读有关此工作所用工具的文档:

  1. 互斥(独占访问资源)
  2. Scoped Locks(用于锁定/解锁Mutex的良好模式)
  3. 信号量(在线程之间传递信息)
  4. ReadWrite Mutex(许多读者,写入时独占访问)
  5. 信号(如何"杀死"一个线程或发送一个中断信号,如何捕获这些信号)
  6. 并行设计模式:老板/工人,生产者/消费者等(见施密特)
  7. 平台特定工具:openMP,C块等

祝好运 !并发很有趣,只需要花时间......

  • 我同意.我发现编写MT代码比单线程更有趣. (3认同)
  • 并发乐趣!! ?? 伙计,你很勇敢:-) (2认同)
  • @Ariel:如果你接受减速,思考并变得富有创造力,这很有趣.就像每一项艰巨的任务一样,如果你给自己做正确的事情所需的时间,这很有趣. (2认同)

rui*_*rui 10

您应该阅读有关锁,互斥锁,信号量和条件变量的信息.

一句忠告,如果您的应用程序具有任何形式的UI,请确保始终从UI线程更改它.如果从后台线程访问它们,大多数UI工具包/框架都会崩溃(或出现意外行为).通常它们提供某种形式的调度方法来在UI线程中执行某些功能.

  • 它并不多.只是UI框架通常是单线程的,只有一个线程甚至被允许与UI交互.从另一个线程访问UI的任何部分是一个错误. (2认同)
  • 您应该阅读的其他内容包括死锁,优先级继承和竞争条件. (2认同)

Ale*_*ler 8

永远不要假设外部API是线程安全的.如果未在其文档中明确说明,请不要从多个线程同时调用它们.相反,将它们的使用限制为单个线程或使用互斥锁来防止并发调用(这与上述GUI库非常相似).

下一点与语言有关.请记住,C++(目前)没有明确定义的线程方法.编译器/优化器不知道是否可以同时调用代码.该volatile关键字对于防止多线程上下文中的某些优化(即缓存CPU寄存器中的内存字段)非常有用,但它不是同步机制.

我建议对同步原语进行boost.不要乱用平台API.它们使您的代码难以移植,因为它们在所有主要平台上具有类似的功能,但细节行为略有不同.Boost通过仅向用户公开常用功能来解决这些问题.

此外,如果两个线程同时写入数据结构的可能性最小,则使用同步原语来保护它.即使你认为它只会在一百万年中发生一次.


小智 7

我发现非常有用的一件事是使应用程序可以根据它用于各种任务的实际线程数进行配置.例如,如果有多个线程访问数据库,请通过命令行参数配置这些线程的数量.这在调试时非常方便 - 您可以通过将数字设置为1来排除线程问题,或者通过将其设置为高数字来强制它们.在计算最佳线程数时,它也非常方便.


Ski*_*izz 6

确保在单CPU系统和多CPU系统中测试代码.

根据评论: -

  • 单插座,单核
  • 单插座,两个核心
  • 单插槽,两个以上内核
  • 两个插座,每个单芯
  • 两个插座,单核,双核和多核cpu的组合
  • 多个插座,单核,双核和多核cpu的组合

这里的限制因素是成本.理想情况下,请专注于代码将要运行的系统类型.

  • 另请注意,多CPU系统可能会揭示在单CPU,多核系统上永远不会发生的竞争条件.核心之间通信的额外延迟会使事情变得颠倒过来. (3认同)
  • 理想情况下,测试> 2个CPU.出于某种原因,2是数学中的"稳定"数字,事情开始变得时髦> 2. (2认同)