【发布时间】:2019-06-23 22:59:42
【问题描述】:
出于某种原因,我需要在 .dll 中有一个全局对象,它管理一个 std::thread。它的实现方式如下:
#include "Header.h"
#include <thread>
#include <condition_variable>
#include <mutex>
class Foo
{
public:
Foo () : m_closeThread (false)
{
m_thread = std::thread (&Foo::ThreadProc, this);
}
~Foo ()
{
{
std::unique_lock<std::mutex> lock (m_mutex);
m_closeThread = true;
}
m_cv.notify_one ();
m_thread.join ();
}
private:
void ThreadProc ()
{
while (true)
{
std::unique_lock<std::mutex> lock (m_mutex);
m_cv.wait (lock, [this] () { return m_closeThread; });
if (m_closeThread)
break;
}
}
private:
bool m_closeThread;
std::thread m_thread;
std::condition_variable m_cv;
std::mutex m_mutex;
};
Foo& GetFoo ()
{
static Foo foo;
return foo;
}
extern "C" void Function ()
{
auto& foo = GetFoo ();
}
但是,当应用程序关闭时,在~Foo 被执行之前,所有工作线程,.dll,都会被杀死,或者正如 MSVS2015 的输出窗口所说: p>
线程 0x1040 已退出,代码为 0 (0x0)。
而且,由于这一事实(Source0、Source1),如果使用 Windows 7(在 Windows 8 及更高版本上不会阻止),应用程序会在 m_cv.notify_one (); 调用上阻塞。
事实上,它在一个特定版本的 Windows 上阻塞,而不是在其他版本上,这让我认为,某种 UB 是罪魁祸首(例如 DLL 卸载排序相关问题,因为如果这样的问题是不可重现的对象不在 .dll 中),但我想不出解决方案,它允许我优雅地关闭线程,同时让对象成为全局对象(因为,需要进行重大的应用程序重组,使其不是全局的)。
那么,可以在线程被 Windows 平台杀死之前优雅地关闭线程吗?
旁注0:
为了示例的完整性,
这是 DLLMain:
#include <Windows.h>
BOOL APIENTRY DllMain (HMODULE, DWORD, LPVOID) { return TRUE; }
这是 .dll 的标头:
#pragma once
extern "C" void Function ();
这是使用上述 .dll 的控制台应用程序:
#include "..\Test\Header.h"
#include <chrono>
#include <thread>
int main ()
{
Function ();
using namespace std::chrono_literals;
std::this_thread::sleep_for (2s);
}
旁注 1:
我目前最多只能使用 C++11(或 MSVS 2015 中存在的任何功能)。
【问题讨论】:
-
本地静态
Foo foo;使此代码存在缺陷。您无法控制对象的生命周期并依赖于动态延迟初始化和动态取消初始化。您应该编写一个专用的 init / uninit 方法,必须在第一次使用任何库函数之前/最后一次使用任何库函数之后调用。另一个问题是您可能会抛出“C”函数。 -
@VTT 本地静态在第一次使用此类对象时被初始化,这是为了推迟初始化直到我们绝对需要它(即使它不是本地的
static,而是全局的,当 DLL 尚未初始化时,会遇到启动线程的问题)。而且,它是全局的,所以一旦初始化,它就会一直运行到应用程序关闭。有单独的未初始化例程,可能是可能的,但我需要在实际代码中调查它的可行性。我试图通过这个问题了解为什么工作线程在对象被破坏之前被杀死......
标签: c++ windows multithreading c++11 dll