Multithreading 在全局变量析构函数中调用thread.join时不返回

Multithreading 在全局变量析构函数中调用thread.join时不返回,multithreading,c++11,stl,Multithreading,C++11,Stl,在VS2013中使用C++11 STL实现异步打印类。 无法获取thread.join返回,没有死锁。 我正在尝试调试,最终发现这个问题可能是由全局/局部类变量声明引起的。这是细节,我不知道为什么会发生 #include <iostream> #include <string> #include <chrono> #include <mutex> #include <thread> #include <condition_vari

在VS2013中使用C++11 STL实现异步打印类。 无法获取thread.join返回,没有死锁。 我正在尝试调试,最终发现这个问题可能是由全局/局部类变量声明引起的。这是细节,我不知道为什么会发生

#include <iostream>
#include <string>
#include <chrono>
#include <mutex>
#include <thread>
#include <condition_variable>
#include "tbb/concurrent_queue.h"
using namespace std;

class logger
{
public:
    ~logger()
    {
        fin();
    }

    void init()
    {
        m_quit = false;
        m_thd = thread(bind(&logger::printer, this));
        //thread printer(bind(&logger::printer, this));
        //m_thd.swap(printer);
    }

    void fin()
    {
        //not needed
        //unique_lock<mutex> locker(m_mtx);
        if (m_thd.joinable())
        {
            m_quit = true;
            write("fin");
            //locker.unlock();

            m_thd.join();
        }
    }

    void write(const char *msg)
    {
        m_queue.push(msg);
        m_cond.notify_one();
    }

    void printer()
    {
        string msgstr;
        unique_lock<mutex> locker(m_mtx);
        while (1)
        {
            if (m_queue.try_pop(msgstr))
                cout << msgstr << endl;
            else if (m_quit)
                break;
            else
                m_cond.wait(locker);
        }
        cout << "printer quit" <<endl;
    }

    bool m_quit;
    mutex m_mtx;
    condition_variable m_cond;
    thread m_thd;
    tbb::concurrent_queue<string> m_queue;
};
为了方便起见,我将thread.join放入类的析构函数中,以确保m_线程可以正常退出。 我考了全班,结果出了点问题。 当类记录器声明为全局变量时,m_thd.join永远不会返回 像这样:

logger lgg;

void main()
{
    lgg.init();
    for (int i = 0; i < 100; ++i)
    {
        char s[8];
        sprintf_s(s, 8, "%d", i);
        lgg.write(s);
    }

    //if first call lgg.fin() here, m_thd can be joined normally
    //lgg.fin();

    system("pause");
    //dead&blocked here and I observed that printer() finished successfully 
}
如果类记录器声明为局部变量,那么似乎一切都正常

更新日期2015/02/27

我试图删除打印机中的std::cout,但程序仍然在同一位置被阻塞,似乎这不是std::cout的问题? 删除fin中的多余锁定
我想知道你是如何使用m_mtx的。正常模式是两个线程都锁定它,两个线程都解锁它。但fin无法锁定它

同样出乎意料的是m_cond.waitm_mtx。这将释放互斥锁,只是它一开始没有被锁定


最后,由于m_mtx没有被锁定,我看不出m_quit=true应该如何在m_thd中可见。

您遇到的一个问题是,当logger::fin调用logger::write时,调用std::condition\u variable::notify\u One,而等待线程所持有的相同std::mutex会被持有

这会导致通知的线程立即再次阻塞,因此打印机线程可能会在销毁时或直到伪唤醒时无限期阻塞

当保持与等待线程相同的互斥时,决不应通知

引自:

通知线程不需要在与等待线程相同的互斥锁上持有锁;事实上,这样做是一种悲观,因为通知线程会立即再次阻塞,等待通知线程释放锁


Globals和statics分别被构造和销毁,因为它发生在装载机锁内部。这是地球上最危险的地方,因为它可能导致死锁或应用程序随机崩溃。因此,永远不要在windows上使用线程原语作为静态。因此,在全局对象的析构函数中处理线程基本上就是在做我们在DllMain中被警告不要做的事情

这座建筑物正在被拆除。不要费心扫地、倒垃圾桶、擦白板。不要在大楼的出口排队,这样每个人都可以把他们的进出磁铁移到外面。你所做的就是让拆迁队等着你完成这些毫无意义的房屋清理任务

:

如果DllMain函数创建了一个线程,然后等待该线程执行某些操作,例如,等待该线程发出一个表示它已完成初始化的事件信号,那么您已经创建了死锁。DllMain中的DLL\u进程\u附加通知处理程序正在等待新线程运行,但新线程无法运行,直到DllMain函数返回,以便它可以发送新的DLL\u线程\u附加通知

这种死锁在DLL_PROCESS_DETACH中更为常见,DLL希望关闭其工作线程,并在卸载自身之前等待它们清除。您不能等待DLL\u进程\u分离中的线程,因为该线程需要在退出之前发送DLL\u线程\u分离通知,而在DLL\u进程\u分离处理程序返回之前,它无法执行此操作

这也发生在使用EXE时,因为VisualC++运行时欺骗和注册它的构造函数和析构函数,在运行时加载或卸载时运行C运行时,因此结束相同的问题:

答案是C运行时库雇佣了一名走狗。雇佣的仆人是C运行库DLL,例如MSVCR80.DLL。EXE中的C运行时启动代码向C运行时库DLL注册所有析构函数,当C运行时库DLL获得其DLL_进程_分离时,它调用EXE请求的所有析构函数


仅供参考,线程打印机bind&logger::printer,this;m_thd.swapprinter;相当于m_thd=thread&logger::printer,this;。这个问题看起来像是由于std::cout在全局记录器实例之前被销毁而导致的未定义行为,因此fin中的最终写调用导致了可怕的事情发生。我会编写一个RAII包装器来调用init和fin。我在析构函数中调用fin的不是RAII设计吗?你能给我举个例子吗?非常感谢~也许你指的是这样的包装器?RaWrapperlogger*pLgg{pLgg->init;}~RaWrapper{m_pLgg->fin;}@Casey@Casey:不,std::cout当时还活着。@Mgetz这是我找到的答案,非常感谢@正如我所解释的,logger::fin有可能永远不会像m_thd.join那样返回
无限期阻塞或直到出现伪wakup。@t例如,您可以通过交换writefin中的行来解决问题;和locker.unlock;在function logger::fin.中,但这似乎只是一种悲观情绪,但并没有导致问题?我试图删除unique_lock lockerm_mtx;开锁;当lgg是全局的,而本地lgg可以正常地进行析构函数时仍然阻塞。非常有用!m_cond将通过写入来取消通知,m_mtx将在这之后解锁。事实上,在fin期间没有必要拥有m_mtx。我修改了代码,但仍然存在上述问题
void main()
{
    logger lgg;
    lgg.init();
    for (int i = 0; i < 100; ++i)
    {
        char s[8];
        sprintf_s(s, 8, "%d", i);
        lgg.write(s);
    }

    system("pause");
}