c++ - thread_local in C++ 11 isn't consistent
问题描述
Why the output isn't consistently 1
, when I declared Counter
object as thread_local
?
#include <iostream>
#include <thread>
#define MAX 10
class Counter {
int c;
public:
Counter() : c(0) {}
void increment() {
++c;
}
~Counter() {
std::cout << "Thread " << std::this_thread::get_id() << " having counter value " << c << "\n";
}
};
thread_local Counter c;
void doWork() {
c.increment();
}
int main() {
std::thread t[MAX];
for ( int i = 0; i < MAX; i++ )
t[i] = std::thread(doWork);
for ( int i = 0; i < MAX; i++ )
t[i].join();
return 0;
}
Output :
Thread 2 having counter value 19469616
Thread 3 having counter value 1
Thread 4 having counter value 19464528
Thread 5 having counter value 19464528
Thread 7 having counter value 1
Thread 6 having counter value 1
Thread 8 having counter value 1
Thread 9 having counter value 1
Thread 10 having counter value 1
Thread 11 having counter value 1
解决方案
我从探索中得到的唯一有趣的事情是 MSVC 的运行方式与 clang/gcc 不同。
这是 MSVC 在最新编译器上的输出:
Thread 34316 having counter value 1
Thread 34312 having counter value 1
Thread 34320 having counter value 1
Thread 34328 having counter value 1
Thread 34324 having counter value 1
Thread 34332 having counter value 1
Thread 34336 having counter value 1
Thread 34340 having counter value 1
Thread 34344 having counter value 1
Thread 34348 having counter value 1
Thread 29300 having counter value 0
c:\dv\TestCpp\out\build\x64-Debug (default)\TestCpp.exe (process 27748) exited with code 0.
注意:“线程 29300 的计数器值为 0”。这是主线。由于 doWork() 从未在主线程中调用过,因此计数器值为 0。这正如我所期望的那样工作。“c”是一个全局计数器对象——它应该在所有线程中创建,包括主线程。
但是,当我使用 rextester.com 在 clang 和 gcc 下运行(并且 Godbolt 似乎不喜欢运行多线程程序,AFAICT)时,我得到:
Thread 140199006193408 having counter value 1
Thread 140198906181376 having counter value 1
Thread 140198806169344 having counter value 1
Thread 140198706157312 having counter value 1
Thread 140199106205440 having counter value 1
Thread 140199206217472 having counter value 1
Thread 140199306229504 having counter value 1
Thread 140199406241536 having counter value 1
Thread 140199506253568 having counter value 1
Thread 140199606265600 having counter value 1
注意:没有计数器值为 0 的主线程。“c”Counter 对象从未在 gcc 和 clang 下的主线程中创建和销毁,但它在 MSVC 下的主线程中创建和销毁。
我相信您的个人错误是您使用的是旧版本的编译器,其中存在错误。无论我使用哪种最新版本的各种类型的编译器,我都无法重现您的错误,但我将向 MSVC 团队提供一个错误,因为这是标准 C++ 并且有一个数字表明它应该产生相同的结果,无论如何编译器的,它没有。
似乎(检查https://en.cppreference.com/w/cpp/language/storage_duration)MSVC 可能做错了,因为 thread_local“计数器 c”对象从未在主线程中显式访问,但该对象是创建和销毁。
我们会看看他们说什么 - 我还有其他一些错误等待他们......
进一步研究:cpp 参考文章似乎表明,因为这是一个“纯全局” - 即它不是一个本地静态线程本地对象,如:
Foo &
GetFooThreadSingleton()
{
static thread_local Foo foo;
return foo;
}
然后他们阅读 cppreference 文章的方式是:它是一个全局的,就像任何其他全局的一样,在这种情况下,MSVC 是正确的。如果我错了,请有人纠正我。谢谢。
另外,如果没有在 gdb 内部的 Ubuntu 中本地运行它并检查,我无法确定全局实际上没有在 gcc/clang 下的主线程中创建和销毁,因为可能是我们出于某种原因错过了该输出。我承认这似乎不太可能。我稍后会尝试这个,因为这个问题让我有点感兴趣。
我在 Ubuntu 中本地运行它并得到了我的预期:只有在主线程中访问全局“计数器 c”时才会创建和销毁它。所以我的想法是,此时这是 gcc/clang 中的一个错误。
推荐阅读
- jquery - 我已经使用 jquery 动态创建了一个表单,现在我在提交表单时遇到了麻烦,对此的任何解决方案将不胜感激
- python - 如何给出文件和文件夹的正确地址?
- php - 添加异常后找不到.htaccess页面
- wordpress - Wordpress 加速 RabbitMQ 队列
- gis - 与使用 PyQGIS 提取的值相比,UI 中的最小最大值不同
- firebase - Advance_pdf_viewer 和 firebase_storage 的颤振依赖问题
- geocoding - 向 Here.com API 结果添加新的兴趣点
- python - 如何从系列中有重复名称的 Pandas DF 创建字典
- performance - 飞镖中的空函数
- bash - 如何扫描条件匹配字符串并在匹配时循环