zal*_*h64 -2 c++ multithreading std
我知道,从根本上讲,在 C++ 中使用类不是线程安全的,因为它们的函数不是原子的,这可能会导致竞争条件。
在学习在类中编写多线程程序时,我们必须包装在互斥体中读取/写入对象的关键部分,以确保原子性。在花时间阅读了一些标准库之后,我很好奇为什么 C++ 不自动为用户执行此操作。
我的理解是,互斥体只包含一个布尔值或整数来表示锁的状态,以及某种形式的队列来表示等待锁的所有线程。鉴于这并不是很大的开销,为什么标准库不自动提供这种抽象并用仅该类本地的互斥体(即不是全局互斥体)包装其所有函数?
即使忽略性能问题(这是很多需要忽略的问题),线程安全也绝不会像“所有对象中都有互斥体”那么简单。
假设您有一个线程安全的容器。有一个共享的容器对象。那么,这段代码是“线程安全的”吗:
shared_container.push_back(value1);
shared_container.push_back(value2);
Run Code Online (Sandbox Code Playgroud)
好吧,这取决于:这段代码的意图是否是这样value1并且value2彼此相邻,则不,不能保证该行为。文字文本不会导致数据争用,但也不会具有所需的行为。
代码的目的是按照它所说的去做。未能做到这一点就是一个错误,无论是由于数据竞争还是违反了代码的预期行为。
那么这个呢:
shared_container.emplace_back(param1, param2);
Run Code Online (Sandbox Code Playgroud)
请记住:emplace_back在函数调用中调用构造函数(容器的元素类型)。如果该构造函数尝试访问会发生什么shared_container?您不能将对象锁定在不同的线程上。
或者更好的是,如果该构造函数需要访问其他共享对象,但持有该对象锁的线程被阻止等待 的锁,会发生什么情况shared_container?您的线程拥有哪个线程并且无法释放它,因为您正处于其中emplace_back?
如果不考虑线程安全,就不可能拥有正常运行的多线程。您可以划分线程间通信,但您永远不能不考虑它。默认情况下使所有对象“线程安全”只会提供安全的假象。更糟糕的是,这使得追踪错误变得更加困难,因为它们更加微妙。
我们甚至可以从标准库中看到更合理的线程安全思想的例子。promise/future是将单个对象从一个线程转发到另一个线程的一种方法。但如果剥离其基本概念,您可以将其视为optional<T>在线程之间共享的“线程安全”。
但它们不是同一件事。
promise/future具有非对称界面。一旦保存该值的代码promise设置了该值,它就无法访问或修改该值。持有该值的代码future无法操作该值;他们只能查看是否已设置或访问它。该接口表示有一个值的提供者和一个值的接收者,并且该值只能走单向。
共享optional<T>没有对这种值转发的接口级支持。如果您有权访问该对象,则可以根据需要设置或获取该值。但这通常不是跨线程数据管理的工作方式,而且与promise/future.
安全的线程间通信最好通过使用专门为此设计的对象来完成。其接口支持特定线程间通信模式的对象,这些对象具有非常严格的线程间交互。