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。

image

由于线程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文件的开头定义为全局变量,输出在类的成员函数中执行,编译没问题,但运行时无输出。为何?

posted @ 2018-05-28 11:37  不锈钢老鼠  阅读(2494)  评论(0编辑  收藏