c++ std::thread 与静态成员挂起

Posted

技术标签:

【中文标题】c++ std::thread 与静态成员挂起【英文标题】:c++ std::thread with static member hangs 【发布时间】:2013-09-13 19:01:20 【问题描述】:

我正在尝试创建一个日志记录类,其中写入日志的调用是静态的。现在,由于性能要求,我想在单独的线程中执行实际的日志记录。由于写入日志的函数是静态的,我认为线程也需要是静态的,这也与另一个执行实际写入日志的静态成员函数相关联。我尝试对其进行编码,但不知何故它在静态线程的初始化过程中挂起。复制该行为的代码示例如下:

“记录器.h”

#ifndef LOGGER_H
#define LOGGER_H

#include <condition_variable>
#include <mutex>
#include <queue>
#include <string>
#include <thread>
#include <vector>

#define LIBRARY_EXPORTS

#ifdef LIBRARY_EXPORTS // inside DLL
#define LIBRARY_API __declspec(dllexport) 
#else // outside DLL
#define LIBRARY_API __declspec(dllimport)
#endif 

using namespace std;

namespace Company  namespace Logging 

class LIBRARY_API Logger

public:
    ~Logger();

    void static Write(string message, vector<string> categories = vector<string>());

private:
    Logger();
    Logger(Logger const&) 
    void operator=(Logger const&) 

    static thread processLogEntriesThread;

    static void ProcessLogEntries();
;



#endif

“记录器.cpp”

#include "Logger.h"

#include <iostream>

using namespace std;

namespace Company  namespace Logging 

thread Logger::processLogEntriesThread = thread(&Logger::ProcessLogEntries);

Logger::Logger()



Logger::~Logger()

    Logger::processLogEntriesThread.join();


void Logger::Write(string message, vector<string> categories)

    cout << message << endl;


void Logger::ProcessLogEntries()




我发现一个奇怪的行为是挂起部分仅在类打包在 DLL 中时发生。如果我将类文件直接使用到控制台 EXE 项目中,它似乎可以工作。

所以基本上我的问题是悬挂部分以及我是否正确地做事。

提前谢谢...

【问题讨论】:

避免在头文件中使用using namespace std;std::cout 不是“线程安全的”。你的线程什么都不做...... 我没有看到任何Logger 实例,所以destructor 没有被调用,也没有join 感谢您对 using namespace std; 的评论;在头文件中。 您需要在Write 调用中使用互斥锁。此外,您实际上是在创建一个单例......一个静态类。当你这样做时,你必须小心初始化,并且对象永远不会真正被破坏。 您在使用 MSVC 吗?可能与此有关:***.com/q/10915233/231299 【参考方案1】:

你可以使用我的记录器库 => https://github.com/PraGitHub/Prapository/tree/master/C_Cpp/Logger

如果发现这篇文章无关紧要,请见谅。

【讨论】:

添加一些上下文来支持您提供的链接。它还可以让您了解链接提供的内容。【参考方案2】:

仅当类打包在 DLL 中时才会发生挂起部分

请参阅Dynamic-Link Library Best Practices 了解挂起原因的完整详细信息:

您不应该在DllMain 中执行以下任务:

致电CreateThread。如果不与其他线程同步,创建线程可以工作,但有风险。

解决方案是为您的记录器库提供一个初始化函数/对象,用户必须在main 中显式调用该函数/对象,而不是在输入main 之前初始化一个全局线程对象。这个函数应该创建线程。

或者使用std::call_once 在第一次记录调用中创建线程。但是,这涉及对每个日志记录调用进行额外的条件检查。这张支票可能很便宜,但不是免费的。

【讨论】:

【参考方案3】:

我看不到记录器线程的任何用法。将线程作为类中的成员并不意味着所有成员函数都将在创建的线程中运行。当您没有记录器实例时,记录器的析构函数将永远不会被调用。 iostream 不是线程安全的!

你必须做什么:

创建某种存储来收集日志信息。这个实例必须是线程安全的! 将来自外界的消息推送到此实例中。实例本身必须有一个自己的线程,该线程从存储中读取数据并将数据放到输出中。这也必须以线程安全的方式完成,因为读取和写入来自不同的线程!

【讨论】:

我承认代码不是很完整,也没有完全的意义,因为我更专注于演示悬挂问题。我最初的计划是使用一个包含日志的静态声明队列(并且可能在队列周围加锁)。 “Write()”函数只会将日志推送到队列中。 “processLogEntriesThread”线程将执行实际的日志记录并清空队列。 “cout”只是为了测试挂起是否发生在调用“Write()”之前。无论如何,现在我只是在考虑使用 Boost.Log 2.0。

以上是关于c++ std::thread 与静态成员挂起的主要内容,如果未能解决你的问题,请参考以下文章

c++11的std::thread能用类的成员函数构造一个线程吗?语法怎样?

Visual 2015 C++ 编译器:使用对象调用静态成员不会导致编译器错误 [重复]

C++之静态

C++类中的静态成员变量与静态成员函数的使用

C++——静态成员详解

C++——静态成员详解