C++ 不持有锁的本地静态初始化避免了C+中可能出现的死锁+;11?
本文提出了一种算法,该算法在初始化局部静态变量时不需要保持锁,但仍然使通过变量定义的并发控制流等待初始化完成 该报说,这样做的好处是避免了可能出现的僵局 函数本地静态持续时间对象初始化的核心问题是,包含的函数可能会被并发调用,因此定义可能会并发执行。未能同步可能会引入竞争条件。显而易见的解决方案是同步。问题是,如果同步涉及在初始值设定项执行时持有锁,那么这种同步可能会引入死锁C++ 不持有锁的本地静态初始化避免了C+中可能出现的死锁+;11?,c++,concurrency,c++11,static-variables,C++,Concurrency,C++11,Static Variables,本文提出了一种算法,该算法在初始化局部静态变量时不需要保持锁,但仍然使通过变量定义的并发控制流等待初始化完成 该报说,这样做的好处是避免了可能出现的僵局 函数本地静态持续时间对象初始化的核心问题是,包含的函数可能会被并发调用,因此定义可能会并发执行。未能同步可能会引入竞争条件。显而易见的解决方案是同步。问题是,如果同步涉及在初始值设定项执行时持有锁,那么这种同步可能会引入死锁 有人能举个例子说明上面描述的死锁发生在哪里吗 这是经典死锁的简单扩展,适用于编译器提供一个锁的情况 void A2B()
有人能举个例子说明上面描述的死锁发生在哪里吗 这是经典死锁的简单扩展,适用于编译器提供一个锁的情况
void A2B() { a.Lock(); B(); a.Unlock(); }
void B() { b.Lock(); ...; b.Unlock(); }
void B2A() { b.Lock(); A(); b.Unlock(); }
void A() { a.Lock(); ...; a.Unlock(); }
如果一个线程调用A2B()
,而另一个线程调用B2A()
,则会发生典型的死锁
在静态初始化锁中,编译器提供b
锁
int A() { a.Lock(); ...; a.Unlock(); return 0; }
void B2A() { static int v = A(); }
void A2B() { a.Lock(); B2A(); a.Unlock(); }
如果假设静态初始化周围有锁,则代码会秘密转换为
void B2A() {
if (!initialized) {
b.Lock(); // standard double-check-lock
if (!initialized) v = A();
initialized=true;
b.Unlock();
}
}
一个线程调用
A2B()
,另一个线程调用B2A()
如果要在本地静态初始化期间保持锁,则有两种可能的设计:
mu
)
在design 2下,您将获得死锁,如所述。也就是说,除非在进行初始化时没有实际持有互斥体。这是通过为每个静态设置一个三态标志来实现的,该标志指示以下之一:未初始化、正在初始化、已初始化。并使用全局互斥来设置标志,但在初始化过程中将其解锁。此“经典死锁”也称为锁反转。如果我们在初始化
v
时不持有锁,如果其他人需要等待v
初始化,我们是否会出现相同的死锁?一个线程调用B2A
并进入初始化。另一个线程调用A2B
并锁定a
并调用B2A
,需要等待v
的初始化完成。现在第一个线程调用<代码> A<代码>,需要等到<代码> A<代码>被代码< > A2B< /COD>解锁。原来的C++标准避免了这个问题:“如果你调用B2A两次,并且第一个调用仍然忙着初始化<代码> V<代码>,那么第二个调用只是继续未初始化的<代码> V< /代码>。.啊,这是有道理的。文章还说,GCC使用的是易于死锁的算法。链接的答案证明了这一点。谢谢。这个答案刚刚被否决了两次。有人想详细说明一下吗?还是只是一次随机的、毫无意义的驾车经过?我喜欢提供准确的信息。我相信这个答案是准确的。如果需要详细说明,请询问。或者至少在这里留下一条评论,说明否决投票的理由。几个小时前,有人对我的一些答案/问题进行了否决投票。也许是同一个用户?如果系统认为这是滥用否决权,它将不会计算这些否决票。