在DLL中定义宏ELPP_THREAD_SAFE会导致EasyLogging++初始化死锁的问题

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/Fish_55_66/article/details/50601289

最近在DLL中使用日志库EasyLogging++记录日志时,因为涉及到多线程,所以我定义了宏了ELPP_THREAD_SAFE来启用多线程安全,但是当我以静态方式调用DLL时,我发现应用程序启动后就没反应了,甚至连 main() 之类的入口函数都进不去。一开始根本不知道什么原因导致了这种情况,这个问题困扰了我近两天的时间,通过慢慢分析,终于发现了原来是因为ELPP_THREAD_SAFE宏定义导致日志库EasyLogging++在初始化时出现了死锁的现象。下面的代码简单展示了这样一个会死锁的现象:


DLL头文件代码:

// testDLL.h
#ifndef TEST_LOG_H_
#define TEST_LOG_H_
#define ELPP_AS_DLL
#if defined(EL_EXPORTS)
#  define ELPP_EXPORT_SYMBOLS
#endif
#define ELPP_THREAD_SAFE
#include "easyloggingpp/easylogging++.h"
#endif

/// 测试函数
extern "C" __declspec(dllexport) void Test();

DLL源文件代码:

// testDLL.cpp
#include "testDLL.h"

INITIALIZE_EASYLOGGINGPP

void Test()
{
	LOG(ERROR) << "TEST";
}

静态调用该DLL的代码:

// main.cpp
#include <iostream>
#include "../testDLL.h"

#pragma comment(lib, "testDLL.lib")

int main()
{
	std::cout << "TEST\n";
	Test();
}
在Windos 7系统下,用Visual Studio 2013编译并运行上述代码,你会看不到任何输出。我们知道,在进入main函数之前,应用程序会先加载DLL,而在DLL的源文件中调用宏定义 INITIALIZE_EASYLOGGINGPP 进行日志库初始化,使用std::mutex 类进行多线程安全锁定时就出现了死锁,从而导致应用程序根本无法进入到main函数。这里给出几个链接,同样是因为在DLL中使用了std::mutex 从而导致了死锁的现象:


通过上面的连接,我们可以看到,这种在 DllMain() 使用 std::mutex 导致死锁的情况在Window 8.1 和Windows 10系统中已经得到了解决,但是在Windows 7或者更低的Windows系统中依旧是存在的,微软的解释说是因为 std::mutex 类所需的内存是由相应的库内部分配的,但是在 Dllmain() 中使用std::mutex 所需的CRT库和STL库尚未被加载,从而导致出现了异常死锁。


那么我们在Windows 7系统或者更低版本的系统中,如何使用日志库EasyLogging++记录DLL中的日志呢?仔细看EasyLogging++线程安全部分的代码,我们可以用一个很简单的方法来解决这个问题,就是使用 CRITICAL_SECTION 来代替 std::mutex 类。具体的代码修改如下:



在上图中,红框圈起来的代码就是改动新增的代码,通过在取消ELPP_USE_STD_THREADING宏定义,就可以解决 std::mutex 死锁的现象。



说明:只有应用程序以静态方式调用DLL时才会出现死锁的现象,如果以动态方式调用DLL是不会出现死锁的。

猜你喜欢

转载自blog.csdn.net/Fish_55_66/article/details/50601289