[英]Is notification of threads guaranteed for std::call_once after exceptional call
[英]Why std::call_once behave different in joined and detached threads?
我写了一个小测试项目,看看在执行callable时std :: call_once是否阻塞。 项目的输出允许假设call_once有2个行为:它在分离的线程上阻塞,而在join上不 阻塞 。 我强烈怀疑这不可能成立,但是我无法得出其他结论,请指导我做出正确的结论。
using namespace std;
once_flag f;
mutex cout_sync;
void my_pause()
{
volatile int x = 0;
for(int i=0; i<2'000'000'000; ++i) { x++; }
}
void thr(int id)
{
auto start = chrono::system_clock::now();
call_once(f, my_pause);
auto end = chrono::system_clock::now();
scoped_lock l{cout_sync};
cout << "Thread " << id << " finished in " << (static_cast<chrono::duration<double>>(end-start)).count() << " sec" << endl;
}
int main()
{
vector<thread> threads;
for(int i=0; i<4; i++)
{
threads.emplace_back(thr, i);
threads.back().join();
}
return 0;
}
输出:
Thread 0 finished in 4.05423 sec
Thread 1 finished in 0 sec
Thread 2 finished in 0 sec
Thread 3 finished in 0 sec
将线程更改为分离线程:
for(int i=0; i<4; i++)
{
threads.emplace_back(thr, i);
threads.back().detach();
}
this_thread::sleep_for(chrono::seconds(5));
输出:
Thread 0 finished in 4.08223 sec
Thread 1 finished in 4.08223 sec
Thread 3 finished in 4.08123 sec
Thread 2 finished in 4.08123 sec
Visual Studio 2017
实际上,这与以下事实有关:您在连接的版本中先加入线程,然后再开始下一个线程。
如果该调用引发异常,则将其传播到call_once的调用者,并且不会翻转该标志,以便尝试另一个调用(这种对call_once的调用称为异常)。
这意味着,如果call_once'd函数引发异常,则认为该异常不会被调用,并且对call_once的下一次调用将再次调用该函数。
这意味着整个call_once()受内部互斥量有效保护。 如果正在执行call_once-d函数,则必须阻止其他任何进入call_once()的线程,直到call_once-d函数返回为止。
您一次加入一个线程,因此直到第一个线程中的call_once返回之前,第二个线程才被调用。
您可以同时有效地启动所有四个分离的线程。 实际上,所有四个线程将大致一起输入call_once。
这些线程之一将最终执行被调用的函数。
其他线程将被阻塞,直到被调用函数返回或引发异常。
这实际上意味着所有线程都必须等待。
这与分离的线程无关。
如果将代码的第一个版本更改为先启动所有四个线程,然后再将它们全部加入,则将看到相同的行为。
不同是不一样的。
未分离的线程按顺序运行-代码等待直到一个线程完成,然后再启动下一个线程。 因此,第一个进入等待循环,而其他则没有。
分离的线程同时运行。 其中一个运行等待循环,其他运行直到等待循环结束。
更改非分离线程的代码以同时运行它们。 为此,请将联接移到创建线程的循环之外。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.