在我的服务器模块上,有时log4cxx库使它崩溃。
这是因为 ...
LevelPtr Level::getTrace() {
static LevelPtr level(new Level(Level::TRACE_INT, LOG4CXX_STR("TRACE"), 7));
return level;
}
静态LevelPtr返回null ptr。
我测试了以下代码。
int start_flag = 0;
class test_dummy {
public:
int mi;
test_dummy() : mi(1)
{
std::cout << "hey!\n";
}
static test_dummy* get_p()
{
static test_dummy* _p = new test_dummy();
return _p;
}
};
void thread_proc()
{
int i = 0;
while (start_flag == 0)
{
i++;
}
if (test_dummy::get_p() == 0)
{
std::cout << "error!!!\n";
}
else
{
std::cout << "mi:" << test_dummy::get_p()->mi << "\n";
}
}
void main()
{
boost::thread *pth_array[5] = {0,};
for (int i = 0; i < 5; i++)
{
pth_array[i] = new boost::thread(thread_proc);
}
start_flag = 1;
for (int i = 0; i < 5; i++)
{
pth_array[i]->join();
}
std::cin.ignore();
}
这确实是线程不安全的,但是我很好奇为什么get_p()返回空指针而不是另一个分配的地址。
这是因为在执行new()操作时将该值设置为0吗?
最佳答案
在此代码中,编译器提供了一个竞争条件:
if (!level_initialized)
{
level_initialized = 1;
level = new Level(...);
}
return level;
(它看起来并不完全一样,它更复杂,但我认为您已经大致了解了)
在clang++ 3.5中,似乎有防止这种竞争的锁,但是如果不实际查看编译器生成的代码,就不可能确切地说出发生了什么。但是我怀疑这是会发生什么。
这是clang++ 3.5生成的内容(减去一些杂波)
_Z8getTracev: # @_Z8getTracev
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
cmpb $0, _ZGVZ8getTracevE5level # Guard variable
jne .LBB0_4
leaq _ZGVZ8getTracevE5level, %rdi
callq __cxa_guard_acquire
cmpl $0, %eax
je .LBB0_4
.Ltmp0:
movl $4, %eax
movl %eax, %edi
callq _Znwm # new
.Ltmp1:
movq %rax, -24(%rbp) # 8-byte Spill
jmp .LBB0_3
.LBB0_3: # %invoke.cont
leaq _ZGVZ8getTracevE5level, %rdi
movq -24(%rbp), %rax # 8-byte Reload
movq -24(%rbp), %rcx # 8-byte Reload
movl $0, (%rcx)
movq %rax, _ZZ8getTracevE5level
callq __cxa_guard_release
.LBB0_4: # %init.end
movq _ZZ8getTracevE5level, %rax
addq $32, %rsp
popq %rbp
retq
我修改了代码以将
Level
用作int
等,因此它比从发布的代码中获取的代码更简单。