在我的服务器模块上,有时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等,因此它比从发布的代码中获取的代码更简单。

08-17 03:00