c++ 日志输出库 spdlog 简介(4)- 多线程txt输出日志
在上一节的代码中加入了向文本文件中写入日志的代码:
UINT CMFCApplication1Dlg::Thread1(LPVOID pParam) { try{ size_t q_size = 4096; //queue size must be power of 2 spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::block_retry); auto console = spd::stdout_color_st("console1"); for (int i = 0; i < 10; i++){ Sleep(500); console->info("Thread 1,Count {}",i); Sleep(10); auto daily = spd::basic_logger_mt("basic1", "logs/basic-log.txt"); daily->info("Thread 1,Count {}", i); spdlog::drop("basic1"); } } catch (const spd::spdlog_ex& ex) { std::cout << "Thread 1 Logger failed: " << ex.what() << std::endl; } spdlog::drop("console1"); return 0; } UINT CMFCApplication1Dlg::Thread2(LPVOID pParam) { try{ size_t q_size = 4096; //queue size must be power of 2 spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::block_retry); auto console = spd::stdout_color_st("console2"); //auto daily2 = spd::basic_logger_mt("basic2", "logs/basic-log.txt"); for (int i = 0; i < 10; i++){ Sleep(500); console->info("Thread 2,Count {}", i); auto daily = spd::basic_logger_mt("basic2", "logs/basic-log.txt"); daily->info("Thread 2,Count {}", i); spdlog::drop("basic2"); } } catch (const spd::spdlog_ex& ex) { std::cout << "Thread 2 Logger failed: " << ex.what() << std::endl; } spdlog::drop("console2"); return 0; }
实验表明,两个线程同时运行,由于写入一个的是同一个txt文件basic-log.txt,运行时会发生异常,如下图第二行的 Permission denied。
由于线程1打开了basic-log.txt文件,在其关闭文件也就是drop之前如果线程2也去打开这个文件,就会发生冲突。
如何解决呢?
(1)ex.what()返回const char * 类型,也就是字符串指针,可以在catch中判断异常类型,如果是Permission denied这种类型的异常,可以重新申请输出日志。
(2)或者用一个线程锁,防止两个线程同时访问一个文件。
首先定义两个全局变量:
HANDLE hEvent1 = NULL;
HANDLE hEvent2 = NULL;
然后在按钮函数中初始化两个Event:
void CMFCApplication1Dlg::OnBnClickedButton1() { hEvent1 = CreateEvent(NULL, TRUE, FALSE, NULL);//手动复位,初始为无信号 hEvent2 = CreateEvent(NULL, TRUE, TRUE, NULL);//手动复位,初始为有信号 AfxBeginThread(Thread1, this); AfxBeginThread(Thread2, this); }
在线程函数中设定互锁机制:
UINT CMFCApplication1Dlg::Thread1(LPVOID pParam) { try{ //size_t q_size = 4096; //queue size must be power of 2 //spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::block_retry); auto console = spd::stdout_color_st("console1"); for (int i = 0; i < 10; i++){ Sleep(900); DWORD dReturn = WaitForSingleObject(hEvent2, INFINITE); if (WAIT_OBJECT_0 == dReturn) { console->info(" Event2 signaled ! "); ResetEvent(hEvent2); Sleep(20); } console->info("Thread 1,Count {}",i); auto daily = spd::basic_logger_mt("basic1", "logs/basic-log.txt"); daily->info("Thread 1,Count {}", i); spdlog::drop("basic1"); SetEvent(hEvent1); Sleep(20); } } catch (const spd::spdlog_ex& ex) { std::cout << "Thread 1 Logger failed: " << ex.what() << std::endl; const char * ExceptionTpye = ex.what(); std::cout<<strlen(ExceptionTpye)<<endl; } spdlog::drop("console1"); return 0; } UINT CMFCApplication1Dlg::Thread2(LPVOID pParam) { try{ //size_t q_size = 4096; //queue size must be power of 2 //spdlog::set_async_mode(q_size, spdlog::async_overflow_policy::block_retry); auto console = spd::stdout_color_st("console2"); for (int i = 0; i < 10; i++){ Sleep(500); DWORD dReturn = WaitForSingleObject(hEvent1, INFINITE); if (WAIT_OBJECT_0 == dReturn) { console->info(" Event1 signaled ! "); ResetEvent(hEvent1); Sleep(20); } console->info("Thread 2,Count {}", i); auto daily = spd::basic_logger_mt("basic2", "logs/basic-log.txt"); daily->info("Thread 2,Count {}", i); spdlog::drop("basic2"); SetEvent(hEvent2); } } catch (const spd::spdlog_ex& ex) { std::cout << "Thread 2 Logger failed: " << ex.what() << std::endl; } spdlog::drop("console2"); return 0; }
原理就是:先启动Event2,这样Thread1就可以执行,执行完第一个循环后把Event1置为有信号,Event2为无信号。这样处于等待状态的Thread2就可以执行了,Thread2执行第一个循环之后把Event2置位有信号,Event1置为无信号。
最后发现Thread1和Thread2交替执行!!!!这样子没什么意义吧。。。
放在cpp文件的开头定义为全局变量,输出在类的成员函数中执行,编译没问题,但运行时无输出。为何?