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文件的開頭定義為全局變量,輸出在類的成員函數中執行,編譯沒問題,但運行時無輸出。為何?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM