【发布时间】:2014-05-06 17:33:21
【问题描述】:
std::call_once 是线程安全的,但它也可以重入吗?
我使用 VS2012(调试和发布)进行的测试表明,从单个线程递归调用 std::call_once 是可以的,但如果在单独的线程上进行调用,则会导致死锁。这是std::call_once 的已知限制吗?
#include "stdafx.h"
#include <iostream>
#include <mutex>
#include <thread>
void Foo()
{
std::cout << "Foo start" << std::endl;
std::once_flag flag;
std::call_once( flag, [](){
std::cout << "Hello World!" << std::endl;
});
std::cout << "Foo end" << std::endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
// Single threaded Works
{
std::once_flag fooFlag;
std::call_once( fooFlag, Foo);
}
// Works
// Threaded version, join outside call_once
{
std::once_flag fooFlag;
std::thread t;
std::call_once( fooFlag, [&t](){
t = std::thread(Foo);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
});
t.join();
}
// Dead locks
// Threaded version, join inside call_once
{
std::once_flag fooFlag;
std::call_once( fooFlag, [](){
auto t = std::thread(Foo);
t.join();
});
}
return 0;
}
似乎std:call_once 正在锁定一个静态互斥锁,该互斥锁在函数退出之前不会解锁。在单线程情况下,它可以工作,因为在第二次调用时,该线程已经拥有锁。在线程版本上,它将阻塞,直到第一个调用退出。
我还注意到,如果将Foo() 函数中的std::once_flag 标志更改为static,死锁仍然会发生。
【问题讨论】:
-
在 VS2012 上也得到了确认。 call_once() 似乎有一个内部互斥锁,可以防止您在不同的线程上同时运行多个 call_once(),即使使用不同的 one_flags。这在我的代码中造成了特别严重的死锁。
-
已在 VS 2015 中修复 - 请参阅 DevDiv#1092852 - blogs.msdn.microsoft.com/vcblog/2015/07/14/…