初始化c+安全吗+;来自linux信号处理程序的11函数静态变量?

初始化c+安全吗+;来自linux信号处理程序的11函数静态变量?,linux,c++11,signals,Linux,C++11,Signals,关于此参考代码中[1]处的C++11静态初始化的两个问题(如下)(这是一个经过完整测试的C++11示例程序) 然后它就不会是死锁安全的,因为信号处理程序可能会在主线程锁定mx时锁定它 但还有其他实现,例如: Mutex mx; // global variable std::atomic<bool> done = false; // global variable ... if (!done.load()) { lock(mx);

关于此参考代码中[1]处的C++11静态初始化的两个问题(如下)(这是一个经过完整测试的C++11示例程序)

然后它就不会是死锁安全的,因为信号处理程序可能会在主线程锁定mx时锁定它

但还有其他实现,例如:

Mutex mx;                        // global variable
std::atomic<bool> done = false;  // global variable
...
if  (!done.load()) {
  lock(mx);
  if (!done.load()) {
    foo = InitFoo();
    done.store(true); 
  }
  unlock(mx);
}
Mutex mx;//全局变量
std::atomic done=false;//全局变量
...
如果(!done.load()){
锁(mx);
如果(!done.load()){
foo=InitFoo();
完成。存储(正确);
}
解锁(mx);
}
如果代码路径在信号处理程序运行之前至少完全运行一次,则不会出现死锁


我的问题是c++11(或任何更高版本)标准是否要求在完成代码的初始传递后实现是异步信号安全的(无死锁,又称无锁的)?

如何
静态Foo Foo=InitFoo()gets initialized

它需要在第一次调用
GetFoo()
时初始化,因为您在
InitFoo()
中提到的“复杂初始化”无法在编译时完成:

具有静态存储的块作用域变量的动态初始化 第一次执行持续时间或线程存储持续时间 控件通过其声明传递;考虑这样一个变量 在完成初始化后初始化。如果 初始化通过抛出异常退出,初始化为 未完成,因此将在下次控件进入时重试 宣言如果控件在 变量正在初始化,并发执行将等待 用于完成初始化。85如果控件在初始化变量时递归地重新输入声明,则 行为未定义。

85实现不得在初始值设定项的执行周围引入任何死锁。死锁仍然可能由程序逻辑引起;实现只需避免由于自身的同步操作而导致的死锁。

有了这些,我们就可以开始提问了

问题1:这是否保证安全(无死锁等)?C++11静态初始化涉及锁。如果信号是在main中第一次调用GetFoo()之前/之后/期间传递的呢

不,这不是保证。考虑< <代码> GETFoEL()/<代码>时,从<代码>中第一次调用< <代码>循环:

GetFoo() -> a lock is taken to initialize 'foo'-> a signal arrives [control goes to signal handling function] -> blocked here for signal handling to complete
                                                                                                                                                                                                                                                                                             
--> Handler() -> DoSomething(GetFoo()) -> GetFoo() -> waits here because the lock is unavailable.
                                                                             
(由于'foo'的初始化尚未完成,信号处理程序必须在此处等待——请参阅上面的引文)

因此,在这种情况下(即使没有任何线程),死锁也会发生,因为线程本身被阻塞

问题2:如果在安装信号处理程序之前在[2]处插入对GetFoo()的调用,这是否保证安全

在本例中,根本没有为SIGINT建立信号处理程序。因此,如果
SIGINT
到达,程序将直接退出。SIGINT的默认配置是终止进程。
GetFoo()
的初始化是否正在进行并不重要。所以这很好

案例(1)的基本问题是信号处理程序
handler
不是,因为它调用的
GetFoo()
不是异步信号安全的


Re。更新了静态初始化可能实现的问题:

C++11标准仅保证
foo
的初始化是以线程安全的方式完成的(请参见上面的粗体引号)。但处理信号不是“并发执行”。它更像是“递归重新进入”,因为它甚至可以发生在单线程程序中——因此它是未定义的。即使静态初始化是像第二种方法那样实现的,也可以避免死锁

换句话说:如果静态初始化像第一个方法一样实现,它是否违反了标准?答案是否定的。因此,您不能依赖于以异步信号安全的方式实现静态初始化

如果您确保“…前提是在信号处理程序运行之前至少完全运行了一次代码路径”,那么您可以引入另一个检查,以确保
GetFoo()
是异步信号安全的,而不管静态初始化是如何实现的:

std::atomic<bool> foo_done = false;
static_assert( std::atomic<bool>::is_lock_free );

Foo* GetFoo() {
    if (!foo_done) {
        static Foo foo = InitFoo();   // [1]
        foo_done = true;
        return &foo;
    }
}
std::原子foo_done=false;
静态断言(std::atomic::是无锁的);
Foo*GetFoo(){
如果(!foo_done){
静态Foo-Foo=InitFoo();//[1]
foo_done=true;
return&foo;
}
}

这是一个很好的答案,但它并没有完全回答我想问的问题。问题不在于[2]处插入的对GetFoo()的调用是否正常。问题是:通过在[2]处插入GetFoo(),确保在建立信号处理程序之前已初始化静态foo,我是否可以保证在主线程执行GetFoo()时信号到达时不会发生死锁?如果静态初始化像我澄清的问题一样被实现。如果你能更新你的答案,那么我一定会接受这个答案@我想我误解了问题2。我已经相应地更新了我的答案。谢谢!!非常好而且非常有用的答案!
GetFoo() -> a lock is taken to initialize 'foo'-> a signal arrives [control goes to signal handling function] -> blocked here for signal handling to complete
                                                                                                                                                                                                                                                                                             
--> Handler() -> DoSomething(GetFoo()) -> GetFoo() -> waits here because the lock is unavailable.
                                                                             
std::atomic<bool> foo_done = false;
static_assert( std::atomic<bool>::is_lock_free );

Foo* GetFoo() {
    if (!foo_done) {
        static Foo foo = InitFoo();   // [1]
        foo_done = true;
        return &foo;
    }
}