我可以在多线程C++中安全地使用int吗
Can I use int in multithreaded C++ safely?
我有以下代码。有什么东西可以使它不穿线吗?
class runner
{
public:
volatile int exitFlag;
// construct in thread A
runner()
{
exitFlag = 0;
}
// run it in thread B
void threadFunc()
{
// does not matter when the change is getting here
while (exitFlag == 0)
{
// ... do stuff (not using exitFlag)
}
}
// call it from thread A
void signalThread()
{
exitFlag = 1; // only change one bit;
}
};
我创建了一个runner
对象,并在另一个线程中启动它的threadFunc
,稍后从第一个线程对它调用signalThread
。有人告诉我,在不同的线程中访问同一个变量(至少其中一个是写的)可能会导致读取垃圾值。但显然,在上面的代码中(因为只更改了一位),这并不重要。读/写的顺序也无关紧要。
不要使用volatile
在线程之间共享变量。它没有提供原子性或同步性的适当保证。原子性的缺乏不太可能导致这里的特定问题,尽管从形式上讲,它会导致未定义的行为。缺乏同步意味着线程完全有可能永远看不到变化,并永远继续运行。
在C++11或更高版本中,请使用std::atomic<int>
。该语言的早期版本没有对线程的标准支持,因此您必须使用编译器提供的任何非标准工具。
数据成员不受保护(例如由互斥体保护)这一简单事实使代码不具有线程安全性。几个线程可以访问runner
的同一实例(例如,通过指针或引用),并且可以同时访问exitflag
,因为没有同步机制。
为了回答您评论中的问题:由于int
被定义为CPU的"本机"类型,我相信给定的读写操作是一条机器代码指令,不会被另一个线程中断。不过,我不知道C/C++是否能保证这种行为。因此,在您的场景中,它可能会起作用,尽管在一般情况下您需要同步。
为了澄清所说的话,尽管有点被掩盖了,但这似乎是因为你没有说你理解:
当一个CPU上的值发生更改时,它大多/通常只会写入该CPU的缓存。其他CPU不会看到它——如果不应用同步技术,其他线程就不应该使用完全相同的内存!
这个改变后的记忆单词最终可能会被刷新到主记忆中,但这需要多长时间是完全不可预测的。此外,如果其他CPU认为他们在自己的缓存中有相同的内存字,他们可能不会去寻找另一个副本,直到他们丢弃了那块内存,然后再次获取它。
互斥使用"内存屏障",这基本上是CPU和子系统中的机制,允许线程说"我希望看到所有数据的TRUE值在这个互斥体之前和影响下都发生了变化。因此,当一个线程获取互斥体的锁,修改内存并释放互斥体时,任何其他获取该互斥体的线程都将保证看到所有内存在该互斥体保护下发生了变化"。
您必须在多线程系统中使用互斥或其他同步对象,尤其是在这个多核CPU时代。回到单核心时代,你会逃脱你的提议;当时唯一的风险就是破坏原子性。在单核机器上,即使是一个简单的机器字也可能遭受破坏的读/修改/写周期,更不用说多核了。。。
总结评论(主要来自Mike Seymour)
- 可用时应使用
std::atomic
- 如果没有,那么互斥和内存屏障就进入了作用域
发布的代码包含未定义的行为(由c++标准规定)但可能出现的唯一具体问题是线程B无法在合理的时间内得到更改,因为它们被卡在cpu中。
- 从不同线程使用int64的不同字节安全吗
- 为什么在全局范围内使用"extern int a"似乎不行?
- 为表示一个或多个操作的C++函数的int参数寻找类型安全的替换
- 为什么我的 std::atomic<int> 变量不是线程安全的?
- 最有效的安全方法将 std::map<int, std::shared_ptr> 转换为 std::<Base>map<int, std::shared_ptr<D
- 将无符号的 int 与 std::string::size_type 进行比较是否安全
- 从std::round转换为int是否安全
- 读取即将在没有同步的情况下同时增加的int是安全的吗?
- 有没有一种安全的方法可以迭代 std::unique_ptr<int[]>?
- 将 -1 分配给无符号 int 以获得最大值是否安全
- 在C++中递增未初始化的 int 是否安全
- 我可以制作一个线程安全的 std::atomic<vector<int>>吗?
- 将矢量<int>分配给矢量是否安全<double>?
- 使用 shared_ptrs 对<无符号 int、boost::any> 类型的通用容器的线程安全实现
- 在unsigned int和signed int之间进行安全转换
- 将intptr_t传递给期望int类型的函数是否安全?
- 如果允许将int赋值给float(反之亦然),是否不类型安全?
- 在可变进模板展开中增加int值的安全方法是什么?
- C++像这样将数组从char转换为unsigned int正确且安全吗
- 在 C++03 中通过 int 运输双精度是否安全