带有静态成员的c ++ std :: thread挂起

时间:2013-09-13 19:01:20

标签: c++ c++11

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

“Logger.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

“Logger.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项目,它似乎正在工作。

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

提前致谢...

3 个答案:

答案 0 :(得分:1)

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

如果发现此帖子无关紧要,请原谅我。

答案 1 :(得分:0)

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

你需要做什么:

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

答案 2 :(得分:0)

  

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

有关Dynamic-Link Library Best Practices挂起的详细信息,请参见:

  

您切勿在{{1​​}}内执行以下任务:

     
      
  • 致电DllMain。如果您不与其他线程同步,则可以创建线程,但是这样做很冒险。
  •   

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

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