首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么这个C++静态单例永远不会停止?

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

Stack Overflow用户
提问于 2013-06-14 01:16:17
回答 6查看 3K关注 0票数 17

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

singleton.h:

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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

代码语言:javascript
复制
#include "singleton.h"

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

我已经知道的是:

  • 线程终止
  • 主线程卡在join
  • 中它与静态有关,如果我将构造函数设为公共,并在main()中创建一个单例实例,它将正确终止。

使用Visual Studio 2012。谢谢你的建议。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2013-06-14 04:18:53

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

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

singleton.h

代码语言:javascript
复制
#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

代码语言:javascript
复制
#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())
    {
    }
}

更新

看起来微软已经意识到了这个问题。在VC++论坛上,一位名为"dlafleur“的用户报告了这篇文章:https://connect.microsoft.com/VisualStudio/feedback/details/747145

票数 4
EN

Stack Overflow用户

发布于 2013-06-14 02:48:05

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

在后台线程中,在线程函数终止后,CRT会尝试获取退出锁来执行一些线程终止工作。这将永远阻塞,因为退出锁由主线程持有,该线程正在等待该线程退出。

这是一个由CRT实现引起的简单死锁。底线是你不能在Windows的静态实例析构函数中等待线程终止。

票数 22
EN

Stack Overflow用户

发布于 2013-06-14 02:47:10

我已经追踪到void __cdecl _lock(int locknum) inside mlock.c了。当main()结束时,主线程转到那里并进入临界区EnterCriticalSection( _locktable[locknum].lock );。然后调用单例析构函数,另一个线程试图进入相同的临界区,但无法进入,因此它开始等待主线程离开临界区。主线程依次等待另一个线程。所以我猜这是个bug。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17093164

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档