C++11 静态局部变量和线程

Posted

技术标签:

【中文标题】C++11 静态局部变量和线程【英文标题】:C++11 static local variables and threads 【发布时间】:2013-02-03 06:13:26 【问题描述】:

对于创建/使用 std::threads 的类来说,静态局部变量是否安全?

因为当我使用这样的东西时:

logger& logger::get_instance(void)

    static logger lg;
    return lg;

并尝试退出(强制关闭)可执行文件,它会不正确地崩溃/退出(Visual Studio 2012 调试器甚至崩溃)。

如果我不这样做,程序会在我强制关闭时优雅退出。

这是崩溃时的堆栈调用

        ntdll.dll!77c10dbd()    Unknown
        [Frames below may be incorrect and/or missing, no symbols loaded for ntdll.dll] 
        ntdll.dll!77b7bfdc()    Unknown
        kernel32.dll!75b55bab() Unknown
    >   msvcr110d.dll!__crtCreateThreadpoolWait(void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * pfnwa, void * pv, _TP_CALLBACK_ENVIRON_V1 * pcbe) Line 569  C
        msvcr110d.dll!Concurrency::details::RegisterAsyncWaitAndLoadLibrary(void * waitingEvent, void (_TP_CALLBACK_INSTANCE *, void *, _TP_WAIT *, unsigned long) * callback, void * data) Line 675    C++
        msvcr110d.dll!Concurrency::details::ExternalContextBase::PrepareForUse(bool explicitAttach) Line 120    C++
        msvcr110d.dll!Concurrency::details::ExternalContextBase::ExternalContextBase(Concurrency::details::SchedulerBase * pScheduler, bool explicitAttach) Line 52 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::GetExternalContext(bool explicitAttach) Line 1579    C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::AttachExternalContext(bool explicitAttach) Line 1527 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::CreateContextFromDefaultScheduler() Line 569 C++
        msvcr110d.dll!Concurrency::details::SchedulerBase::CurrentContext() Line 402    C++
        msvcr110d.dll!Concurrency::details::LockQueueNode::LockQueueNode(unsigned int timeout) Line 616 C++
        msvcr110d.dll!Concurrency::critical_section::lock() Line 1017   C++
        msvcp110d.dll!mtx_do_lock(_Mtx_internal_imp_t * * mtx, const xtime * target) Line 65    C++
        msvcp110d.dll!_Mtx_lock(_Mtx_internal_imp_t * * mtx) Line 144   C++
        escobar.exe!std::_Mtx_lockX(_Mtx_internal_imp_t * * _Mtx) Line 68   C++
        escobar.exe!std::_Mutex_base::lock() Line 43    C++
        escobar.exe!std::unique_lock<std::mutex>::unique_lock<std::mutex>(std::mutex & _Mtx) Line 228   C++
        escobar.exe!escobar::utilities::blocking_queue<escobar::logging::log_message *>::interrupt() Line 71    C++
        escobar.exe!escobar::logging::log_worker::~log_worker() Line 17 C++
        escobar.exe!escobar::logging::log_worker::`scalar deleting destructor'(unsigned int)    C++
        escobar.exe!escobar::logging::logger::close() Line 72   C++
        escobar.exe!escobar::logging::logger::~logger() Line 27 C++
        escobar.exe!`escobar::logging::logger::get_instance'::`2'::`dynamic atexit destructor for 'lg''()   C++
        msvcr110d.dll!doexit(int code, int quick, int retcaller) Line 585   C
        msvcr110d.dll!_cexit() Line 410 C
        msvcr110d.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 296  C
        msvcr110d.dll!_CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved) Line 210   C
        ntdll.dll!77bb2846()    Unknown
        ntdll.dll!77bb2893()    Unknown
        ntdll.dll!77bc09c8()    Unknown
        ntdll.dll!77bc08ad()    Unknown
        KernelBase.dll!75525bbb()   Unknown
        KernelBase.dll!75525c51()   Unknown
        kernel32.dll!75b58543() Unknown
        ntdll.dll!77bbac69()    Unknown
        ntdll.dll!77bbac3c()    Unknown

这里有几个函数

log_worker::~log_worker(void)

    this->queue.interrupt();
    service.join();


void log_worker::run(void)

    while (true)
    
        log_message* msg;
        if (this->queue.dequeue(msg) == false)
            break;

        this->lg->print_log_message(msg);

        delete msg;
    



    bool dequeue(T& item)
    
        std::unique_lock<std::mutex> lock(m);

        // handles spurious wakeups
        while (!this->data_available && !this->interrupted)
            cv.wait(lock);

        if (this->interrupted)
            return false;

        item = std::move(this->front());
        this->pop();
        if (this->empty())
            this->data_available = false;
        return true;
    

    void interrupt(void)
    
        std::unique_lock<std::mutex> lock(m);
        this->interrupted = true;
        cv.notify_all();
        printf("notified threads...\n");
    

【问题讨论】:

C++ 标准对销毁具有静态存储持续时间的对象的顺序做出了哪些保证(如果有的话)? 您还可以通过在 Visual Studio 中启用 Microsoft 符号服务器来获取更多信息。 through-the-interface.typepad.com/.a/… @TheAJ 也是你的log_worker 静态的吗? @ta.speot.is:它们以与它们构建时相反的顺序被销毁。 @TheAJ - 在关机时停止删除内容。 【参考方案1】:

看起来你有一个分离的线程SchedulerBase可能是任何仍使用logger的调度线程),当你的应用程序停止并且logger被破坏时它仍在运行,这导致崩溃。

并且log_worker是在logger类中动态分配的。

您需要确保所有使用logger 实例的线程在logger 被销毁之前正确关闭 删除记录器析构函数中的 log_worker

【讨论】:

确保“在销毁记录器之前正确关闭所有使用记录器实例的线程”的最简单方法是不显式销毁记录器。【参考方案2】:

我只需要在关机时停止删除内容。当您使用“X”按钮关闭控制台时,这不是正确的关闭,因此尝试关闭线程是没有意义的。

【讨论】:

以上是关于C++11 静态局部变量和线程的主要内容,如果未能解决你的问题,请参考以下文章

C++11中的局部静态变量初始化线程安全吗? [复制]

C++0x 和静态局部变量的性能损失?

java 局部静态变量在多线程环境下是不是有线程安全问题??

内联函数的局部静态/线程局部变量?

C语言中静态局部变量的问题

lua脚本有静态局部变量吗