在《行动2》中的书籍 c ++并发中,作者引入了一种使用 call_once
函数的方式
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag,init_resource); #1
resource_ptr->do_something();
}
在此[答案] [1]中解释了原因。我曾经使用 atomic_flag
在多线程程序中进行初始化,诸如此类:
td::atomic_flag init = ATOMIC_FLAG_INIT;
std::atomic<bool> initialized = false;
void Init()
{
if (init.test_and_set()) return;
DoInit();
initialized = true;
}
void Foo(){
if(!initialized) return;
DoSomething(); // use some variable intialized in DoInit()
}
每个线程都会调用 init()
>。
阅读本书后,我想知道上述模式会导致种族状况,因此使用不安全?编译器是否有可能在 doinit()
完成之前重新排序指令和初始化
?
[1]:在双重检查的锁定中解释种族条件
In book C++ Concurrency in Action 2nd, 3.3.1, the author introduced a way using call_once
function to avoid double-checked locking pattern when doing initialization in multi-thread program,
std::shared_ptr<some_resource> resource_ptr;
std::once_flag resource_flag;
void init_resource()
{
resource_ptr.reset(new some_resource);
}
void foo()
{
std::call_once(resource_flag,init_resource); #1
resource_ptr->do_something();
}
the reason is explained in this [answer][1]. I used to use atomic_flag
to do initialization in multi-thread program, something like this:
td::atomic_flag init = ATOMIC_FLAG_INIT;
std::atomic<bool> initialized = false;
void Init()
{
if (init.test_and_set()) return;
DoInit();
initialized = true;
}
void Foo(){
if(!initialized) return;
DoSomething(); // use some variable intialized in DoInit()
}
every threads will call Init()
before call Foo()
.
After read the book, I wonder will the above pattern cause race condition, therefore not safe to use? Is it possible that the compiler reorder the instructions and initialized
become true before DoInit()
finish?
[1]: Explain race condition in double checked locking
发布评论
评论(1)
当线程1输入
doInit
和Thread 2跳过并继续进行foo
时,代码中的竞赛条件会发生。您使用
进行处理,如果(!初始化)返回
infoo
,但这并非总是可能的:您应该始终期望一种方法不小心做任何事情,您可能会忘记添加这样的方法检查其他方法。使用
std :: Call_once
并并发调用,只有在执行操作后,执行才会继续。关于重新排序,Atomics操作使用 nofollow noreferrer'> remote_order_serd_seq_cst 默认情况下,允许任何重新排序。
The race condition in your code happens when thread 1 enters
DoInit
and thread 2 skips it and proceeds toFoo
.You handle it with
if(!initialized) return
inFoo
but this is not always possible: you should always expect a method to accidently do nothing and you can forget to add such checks to other methods.With
std::call_once
with concurrent invocation the execution will only continue after the action is executed.Regarding reordering, atomics operations use memory_order_seq_cst by default that does not allow any reordering.