zoukankan      html  css  js  c++  java
  • 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文件的开头定义为全局变量,输出在类的成员函数中执行,编译没问题,但运行时无输出。为何?

  • 相关阅读:
    KNN算法--物以类聚,人以群分
    朴素贝叶斯算法原理
    17.Letter Combinations of a Phone Number
    103.Binary Tree Zigzag Level Order Traversal
    65、使用互斥锁唤醒指定线程
    64、线程之间的通信
    63、使用Timer类来实现定时任务
    62、单例模式
    61.volatile关键字
    60、死锁
  • 原文地址:https://www.cnblogs.com/oucsheep/p/9099291.html
Copyright © 2011-2022 走看看