C++ c+中的静态线程本地内存异步信号安全吗+;14?
假设我有这个函数:C++ c+中的静态线程本地内存异步信号安全吗+;14?,c++,c++11,signals,c++14,C++,C++11,Signals,C++14,假设我有这个函数: int my_thread_id(){ static int counter {0}; thread_local int tid{++counter}; return tid; } 即使在第一次调用时,此函数(my_thread_id)异步信号是否安全 没有 信号处理程序不知道它们在哪个线程上执行,因此thread\u local在那里没有有效的语义。[简介.多线程]p2: 由于调用raise函数而执行的信号处理程序与调用raise函数属于同一执行线程。否则,将
int my_thread_id(){
static int counter {0};
thread_local int tid{++counter};
return tid;
}
即使在第一次调用时,此函数(my_thread_id)异步信号是否安全 没有
信号处理程序不知道它们在哪个线程上执行,因此thread\u local
在那里没有有效的语义。[简介.多线程]p2:
由于调用raise
函数而执行的信号处理程序与调用raise
函数属于同一执行线程。否则,将不指定哪个执行线程包含信号处理程序调用
与此相关的还有第23页:
如果发生以下情况,则两个操作可能同时进行:
- 它们由不同的线程执行,或者
- 它们是不排序的,并且至少有一个由信号处理程序执行
volatile sig_atomic_t
,此处不适用。)
第二个项目符号与[intro.execution]p6有关:
如果信号处理程序是由于调用raise
函数而执行的,则处理程序的执行顺序在调用raise
函数之后及其返回之前。[注:当由于其他原因接收到信号时,信号处理程序的执行通常与程序的其余部分无关。-结束注]
该答案可被视为对ildjarn给出的答案的补充。严格地讲C++ 14标准时,使用来自信号处理程序的THEADLILL本地存储的数据导致未定义的行为。 然而,在某些平台上,这种使用可能是允许的。例如,大多数POSIX系统通过使用分配给每个线程(如堆栈)的特殊数据段来实现线程本地存储。有关详细说明,请参阅。在这种情况下,对线程本地数据的访问是异步信号安全的,因为它不涉及任何锁 但是,信号处理程序读取或写入的数据可能仍然不一致,除非仅访问原子或使用
std::atomic_signal_fence
对访问进行防护。这是因为编译器不知道信号处理程序何时会中断执行,从而可能会对读写指令重新排序std::atomic_signal_fence
禁止此重新排序,CPU的重新排序不是问题,因为执行发生在同一线程内,并且仅当结果(线程内)与指令按顺序执行时相同时,才允许CPU重新排序指令
除了std::atomic_signal_fence之外,使用std::atomic类型的变量是安全的,只要它们是无锁的(如std::is_lock_free所示)
在Linux(我相信大多数其他POSIX平台)上,信号是否被发送到特定线程的问题取决于信号的生成方式和信号的确切类型。例如,
SIGSEGV
和SIGBUS
始终被调度到导致错误的线程,从而产生信号。在这种情况下,使用线程本地存储可以方便地从此类错误中恢复。但是,在将代码保持在支持C++标准的所有平台上的情况下,无法做到这一点。这个答案中提到的文档指定TLS数据的地址可以通过调用_TLS_get_addr()来确定,如果内存尚未设置,该函数可以调用malloc(),因此使用锁。不完全确定编译器在什么条件下发出对_tls_get_addr()的调用,_tls_get_addr()在什么条件下调用malloc(),但我确实看到了这种情况(使用加载的插件)。可能解决方法是确保在信号触发之前访问TLS数据-这可以防止出现malloc()情况。