首页 文章

为什么这个C静态单例永远不会停止?

提问于
浏览
17

我在C中实现了单例(静态版本) . 我知道关于这种模式和潜在的线程安全问题的所有争议,但我很好奇为什么这个确切的实现不会停止 . 该程序永不退出,最终仍处于死锁状态 .

singleton.h:

#pragma once
#include <thread>
#include <atomic>

class Singleton
{
public:
    static Singleton& getInstance();

private:
    std::thread mThread;
    std::atomic_bool mRun;

    Singleton();
    ~Singleton();
    void threadFoo();
};

singleton.cpp

#include "singleton.h"

Singleton& Singleton::getInstance()
{
    static Singleton instance;
    return instance;
} 

Singleton::Singleton()
{
    mRun.store(true);
    mThread = std::thread(&Singleton::threadFoo, this);
}

Singleton::~Singleton()
{
    mRun.store(false);

    if(mThread.joinable())
        mThread.join();
}

void Singleton::threadFoo()
{
    while(mRun)
    {
    }
}

main.cpp中

#include "singleton.h"

int main()
{
    Singleton::getInstance();
    return 0;
}

我所知道的:

  • 线程终止

  • 主线程卡在连接中

  • 它与静态有关,如果我将构造函数设为public并在main()中创建一个Singleton实例,它将正确终止 .

使用Visual Studio 2012.感谢您的建议 .

5 回答

  • 4

    我已将其追溯到 mlock.c 内的 void __cdecl _lock(int locknum) . 当 main() 结束时,主线程进入并进入临界区 EnterCriticalSection( _locktable[locknum].lock ); . 然后调用Singleton析构函数,另一个线程尝试进入相同的临界区,但是't, and so it starts waiting for main thread to leave the critical section. Main thread, in turn, waits for the other thread. So I guess it'是一个错误 .

  • 7

    请参阅标准中的[basic.start.term]:

    如果在信号处理程序(18.10)中不允许使用标准库对象或函数,那么在(1.10)完成销毁具有静态存储持续时间的对象并执行std :: atexit注册函数之前不会发生这种情况(18.5) ,程序有未定义的行为 . [注意:如果在对象销毁之前没有使用具有静态存储持续时间的对象,则程序具有未定义的行为 . 在调用std :: exit或从main退出之前终止每个线程足以满足这些要求,但不是必需的 . 这些要求允许线程管理器作为静态存储持续时间对象 . - 尾注]

  • 1

    在主线程上,在 main() 终止后,CRT获取退出锁并调用静态实例析构函数,等待后台线程退出 .

    在后台线程上,在线程函数终止后,CRT尝试获取退出锁以执行一些线程终止工作 . 这会永远阻塞,因为退出锁是由主线程保持的,它正在等待该线程退出 .

    这是由CRT实现引起的简单死锁 . 最重要的是,您不能在Windows上的静态实例析构函数中等待线程终止 .

  • 4

    好的,谢谢大家的提示 . 显然,这种模式实现会导致VC出现死锁 .

    在做了一些进一步的研究之后,我发现这个实现基于C 11机制,它在VC中工作 .

    singleton.h

    #pragma once
    #include <thread>
    #include <atomic>
    #include <memory>
    #include <mutex>
    
    
    class Singleton
    {
    public:
        static Singleton& getInstance();
        virtual ~Singleton();
    
    private:
        static std::unique_ptr<Singleton> mInstance;
        static std::once_flag mOnceFlag;
        std::thread mThread;
        std::atomic_bool mRun;
    
        Singleton();
    
        void threadFoo();
    };
    

    singleton.cpp

    #include "singleton.h"
    
    std::unique_ptr<Singleton> Singleton::mInstance = nullptr;
    std::once_flag Singleton::mOnceFlag;
    
    
    Singleton& Singleton::getInstance()
    {
        std::call_once(mOnceFlag, [] { mInstance.reset(new Singleton); });
        return *mInstance.get();
    }
    
    
    Singleton::Singleton()
    {
        mRun.store(true);
        mThread = std::thread(&Singleton::threadFoo, this);
    }
    
    Singleton::~Singleton()
    { 
        mRun.store(false);
    
        if(mThread.joinable())
            mThread.join();
    }
    
    void Singleton::threadFoo()
    {
        while(mRun.load())
        {
        }
    }
    

    UPDATE

    看起来微软已经意识到了这个问题 . 在VC论坛中,名为"dlafleur"的用户报告了此帖:https://connect.microsoft.com/VisualStudio/feedback/details/747145

  • 22

    这个死锁bug与中的相同

    std::thread::join() hangs if called after main() exits when using VS2012 RC

    并且它在Visual Studio 2013中不是固定的 .

相关问题