我在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;
}
我已经知道的是:
使用Visual Studio 2012。谢谢你的建议。
发布于 2013-06-14 04:18:53
好的,谢谢大家的提示。显然,这种模式的实现会导致VC++上的死锁。
在做了一些进一步的研究之后,我发现这个实现是基于在VC++中工作的C++11机制的。
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())
{
}
}
更新
看起来微软已经意识到了这个问题。在VC++论坛上,一位名为"dlafleur“的用户报告了这篇文章:https://connect.microsoft.com/VisualStudio/feedback/details/747145
发布于 2013-06-14 02:48:05
在主线程上,在main()
终止后,CRT获取退出锁并调用静态实例析构函数,该函数等待后台线程退出。
在后台线程中,在线程函数终止后,CRT会尝试获取退出锁来执行一些线程终止工作。这将永远阻塞,因为退出锁由主线程持有,该线程正在等待该线程退出。
这是一个由CRT实现引起的简单死锁。底线是你不能在Windows的静态实例析构函数中等待线程终止。
发布于 2013-06-14 02:47:10
我已经追踪到void __cdecl _lock(int locknum)
inside mlock.c
了。当main()
结束时,主线程转到那里并进入临界区EnterCriticalSection( _locktable[locknum].lock );
。然后调用单例析构函数,另一个线程试图进入相同的临界区,但无法进入,因此它开始等待主线程离开临界区。主线程依次等待另一个线程。所以我猜这是个bug。
https://stackoverflow.com/questions/17093164
复制相似问题