2013-11-04 52 views
0

我試過並試圖找到一個關於這個在SO和互聯網上的一般預先討論。也許我做得不對,因爲很難相信這個基本的C語言沒有解決這個問題。使用POSIX線程進行重大日誌記錄的理想方法?

我有一些計算簡單的代碼,但仍需要大量的日誌記錄才能執行。此日誌記錄正在使我的代碼運行速度降低10倍。顯然,解決方案是將日誌記錄分解成單獨的線程或進程。使用單獨的進程需要大量特定於平臺的mumbo-jumbo,我需要此代碼保持儘可能的便攜性(目前正在OS X中開發,稍後將移植到Windows中)。所以我剛開始學習線程和pthread.h。

要明確一點,與Internet上的一些現有pthread討論不同,我們正在討論直接寫入文件而不是I/O。日誌文件永遠不需要被程序讀取。我在做什麼,此刻是:

/* Called many times a second; a few minutes of program usage will make 
a couple megs of text in the log file */ 
void MyLogFunction(char *format, ...) 
{ 
    char buffer[1024]; 
    va_list arglist; 

    va_start(arglist, format); 
    vsnprintf(buffer, sizeof(buffer) - 1, format, arglist); 
    va_end(arglist); 

    pthread_t threadID; 
    pthread_create(&threadID, NULL, WriteLog, &buffer); 
} 

/* As written, this function writes unpredictable contents to disk because 
I passed it a pointer to a local variable that is getting modified 
constantly by the main thread; I know I should be doing something like 
allocating new blocks of memory within MyLogFunction() for each pthread, 
so let's pretend I'm doing that and move on with the discussion ;-) */ 
void *WriteLog(void *string) 
{ 
    pthread_detach(pthread_self()); 
    fprintf(gLogHandle, "%s: %s\n", 
     /* OS X time-to-string stuff omitted here */, 
     (char *)string); 
    fflush(gLogHandle); 

    return NULL; 
} 

我擔心兩件事情:

答:這似乎是不明智的產卵多達幾十並行線程每一秒的。是嗎?

B.有時郵件可能會亂寫,對吧?

這使我考慮以下三個問題:

  1. 我應該做一個記錄線程保持打開狀態?如何知道什麼時候不用輪詢而寫另一行?我不能悠閒地輪詢一些gNewLineIsAvailableToWrite布爾,因爲這段代碼有時會崩潰,我不能錯過崩潰前的最後一條記錄消息。另外,在日誌中創建新行可以使每隔幾個刻度觸發一個峯值,即每個刻度可能有20行。

  2. 我應該按照當前顯示的方式製作單獨的pthread,但是給他們彼此的ID並告訴他們pthread_join(),以便他們在寫入他們的行之前等待前一個線程完成?這仍然導致線程氾濫,但至少他們會按順序寫入,呃?

  3. 或者我在看這個問題都不對?

+0

寫入文件**是** I/O。 – SirDarius

+0

您不必將輸入和輸出都作爲I/O進行計數 - 要麼計數。 –

+0

啊,好吧。然後,我沒有說得那麼好,但是我想把這個問題與我所看到的關於使用線程讀入和解析數據的討論區分開來,這是一個比我想解決的更復雜的問題。 – user1134918

回答

2

1)是的。當你使用某種形式的線程間通信時,它會知道何時寫另一行,以允許它在生產者 - 消費者隊列中等待log * structs。

2)NO !!不要不斷地創建/終止/銷燬線程。不要靠近join()。只需在啓動時創建一個日誌記錄線程,並將日誌請求排隊。

或者,當然,找一個記錄LIB已經工程:)

另一個提示:你可以一個「命令」枚舉添加到您的結構,告訴記錄線程做什麼。當然,它的大部分時間都是'將字符串記錄在緩衝區中',但是您可以添加其他命令來刷新日誌文件,在緩衝區中使用新路徑/文件名打開新日誌文件,打開新的日誌文件在X MB或每X小時之後,無論如何。隊列的「鬆弛」將允許「冗長的」文件操作,而不會影響請求日誌記錄的線程的性能。

+0

好的,我很高興我問了這個問題。我完全忘了查看線程間通信的主題。我昨天整天都在讀IPC,所以我應該想到這一點。看起來我需要閱讀關於pthread_cond_wait()和pthread_cond_signal()。 – user1134918

1

單獨創建一個線程每當你計劃記錄一條線時,線程顯然是不理想的(更不用說受到來自多個線程的損壞輸出的影響)。 一個登錄到多線程應用程序的方法是登錄到一個內部(通常是循環)緩衝區,該緩衝區由特定的線程定期刷新。因此,登錄每個線程的「I/O」成本大幅下降(儘管格式化仍然很費力)。但是,您必須支付競爭日誌資源(緩衝區)的費用。還有一些方法可以緩解它,但一些成本仍然存在。

說完所有上述內容,就很難評估處理它的正確方法,而無需查看程序的性能概況 - 多少線程,他們爭奪什麼等。如果我是你, d從開始嘗試即可使用的日誌記錄基礎結構,如log4cpp。很多人已經解決了你所面臨的問題,沒有什麼理由再重新發明它。嘗試使用它,然後看看你的瓶頸在哪裏。

+0

感謝您的建議。畢竟我可能會使用這樣的庫(我也看到了「log4c」,對於像我這樣的穴居人)。不幸的是,一個是LGPL,我不知道我是否應該開源我寫的代碼,但也許我會。 – user1134918

0
  1. 我應該讓一個日誌線程保持打開嗎?

是的,通常的方法是有一個單一的,長壽命的日誌記錄線程。

...它怎麼知道什麼時候不用快速輪詢寫另一行?

標準解決方案是使用由互斥保護的共享日誌隊列,和相關聯的條件變量喚醒記錄線程(最佳,你只需要這麼做時停止成爲空隊列)。

...此外,在日誌中創建新行會使每個tick的峯值可能達到20行。

儘量確保任何共享使用能夠吸收這些尖峯隊列,並注意有就是爲什麼記錄線程必須只消耗每鎖/等待/解鎖週期一個消息沒有理由。