gpt4 book ai didi

c++ - 可以从 Windows DLL 中的全局变量创建/销毁 std::threads 吗?

转载 作者:行者123 更新时间:2023-12-04 19:16:12 26 4
gpt4 key购买 nike

我正在创建一个日志记录对象,它在一个单独的 std::thread 上执行真正的文件写入工作,并提供一个到日志命令缓冲区的接口(interface),同步调用者线程和一个 worker 线。对缓冲区的访问受互斥锁保护,工作线程退出条件有一个原子 bool 值,我使用 Windows native 事件作为信号,在新命令到达时唤醒工作线程。该对象的构造函数生成工作线程,因此它立即可用。工作线程只是一个 while 循环检查退出条件,在循环中阻塞等待信号。对象的析构函数最终只是设置退出条件,通知线程唤醒并加入它以确保它在对象被完全销毁之前关闭。

看起来很简单,当在函数中的某处使用这样的对象时,效果很好。但是,当将这样的对象声明为全局变量以使其对每个人都可用时,它会停止工作。我在 Windows 上,使用 Visual Studio 2017 和 2015 工具链。我的项目是另一个应用程序的 DLL 插件。

到目前为止我尝试过的事情:

  • 在全局对象的构造函数中启动线程。但是,这会使主线程在我的 DLL 加载时立即挂起。在调试器中暂停应用程序显示我们在 std 库中,此时主线程应该启动工作线程并且现在卡在等待条件变量,大概是一旦工作线程发出信号启动了吗?
  • 当我们第一次从其他地方使用全局对象时,延迟-按需构建线程。这种构建它的方式很顺利,没有挂起。但是,当向工作线程发出从析构函数退出的信号时,信号已发送,但工作线程上的连接现在挂起。在调试器中暂停应用程序会发现我们的主线程是唯一还活着的线程,工作线程已经消失了?在右大括号之前放置在工作线程函数中的断点表明它永远不会被击中;线程必须被杀死?
  • 我还尝试通过 std::future 启动线程,以异步方式启动它,并且从全局对象中的构造函数启动它非常好。然而,当 future 试图加入析构函数中的线程时,它也会挂起;这里再次没有检测到工作线程,同时没有断点被击中。

可能发生了什么?我无法想象这是因为线程构造和销毁发生在 main() 之外可以这么说;这些 std 基元在这种时候真的应该可用,对吧?或者这是 Windows 特定的,是在 DllMainDLL_PROCESS_ATTACH/DLL_THREAD_ATTACH 事件的上下文中运行的代码,启动线程可能会造成严重破坏由于线程本地存储尚未启动和运行等原因? (会吗?)

编辑——添加了代码示例

以下是我的代码的缩写/简化;它可能甚至无法编译,但我希望它能说明问题:)

class LogWriter {
public:
LogWriter() :
m_mayLive(true) {
m_writerThread = std::thread(&C_LogWriter::HandleLogWrites, this); // or in initializer list above, same result
};
~LogWriter() {
m_mayLive = false;
m_doSomething.signal();
if (m_writerThread.joinable()) {
m_writerThread.join();
}
};
void AddToLog(const std::string& line) { // multithreaded client facing interface
{
Locker locker; // Locker = own RAII locker class
Lock(locker); // using a mutex here behind the scenes
m_outstandingLines.push_back(line);
}
m_doSomething.signal();
}

private:
std::list<std::string> m_outstandingLines; // buffer between worker thread and the rest of the world
std::atomic<bool> m_mayLive; // worker thread exit signal
juce::WaitableEvent m_doSomething; // signal to wake up worker thread; no std -- we're using other libs as well
std::thread m_writerThread;

int HandleLogWrites() {
do {
m_doSomething.wait(); // wait for input; no busy loop please

C_Locker locker; // access our line buffer; auto-released at end of loop iteration
Lock(locker);

while (!m_outstandingLines.empty()) {
WriteLineToLog(m_outstandingLines.front());
m_outstandingLines.pop_front();
if (!m_outstandingLines.empty()) {
locker.Unlock(); // don't hog; give caller threads some room to add lines to the buffer in between
std::this_thread::sleep_for(std::chrono::milliseconds(10));
Lock(locker);
}
};
} while (m_mayLive); // atmoic bool; no need to mutex it

WriteLineToLog("LogWriter shut down"); // doesn't show in the logs; breakpoints here also aren't being hit
return 0;
}

void WriteLineToLog(const std::string& line) {
... fopen, fprintf the line, flush, close ...
}

void Lock(C_Locker& locker) {
static LocalLock lock; // LocalLock is similar to std::mutex, though we're using other libs here
locker.Lock(&lock);
}
};



class Logger {
public:
Logger();
~Logger();
void operator() (const char* text, ...) { // behave like printf
std::string newLine;
... vsnprintf -> std::string ...
m_writer.AddToLog(newLine);
}

private:
LogWriter m_writer;
};



extern Logger g_logger; // so everyone can use g_logger("x = %d\n", x);
// no need to make it a Meyer Singleton; we have no other global objects interfering

最佳答案

由于您正在用 C++ 编写 DLL,因此您必须了解 DLL 中的“全局变量”是如何工作的。编译器将它们的初始化粘贴到 DllMain 中,然后再执行其他操作。但是在 DllMain 中可以执行一些严格的规则,因为它在加载程序锁定下运行。简短的总结是您不能在另一个 DLL 中调用任何东西,因为当您的 DllMain 正在运行时无法加载该 DLL。绝对不允许调用 CreateThread,即使包装在 std::thread::thread 构造函数中也是如此。

析构函数的问题很可能是因为您的 DLL 已经退出(没有代码无法判断)。 DLL 在 EXE 之前卸载,并且它们各自的全局变量也按该顺序清理。由于显而易见的原因,任何从 EXE 中的析构函数登录的尝试都会失败。

这里没有简单的解决方案。 Andrei Alexandrescu 的“Modern C++ Design”对于在非 DLL 情况下进行日志记录有一个合理的解决方案,但您需要强化它才能在 DLL 中使用。如果您的记录器仍然存在,另一种方法是检查您的记录功能。您可以为此使用命名互斥体。如果您的日志功能在 OpenMutex 中失败,则说明记录器尚不存在或不再存在。

关于c++ - 可以从 Windows DLL 中的全局变量创建/销毁 std::threads 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60376154/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com