2011-02-08 51 views
4

假設我正在設計一個簡單的日誌記錄類(是的 - 我知道知道已經有那些已經在野外!),我想這個類是靜態的,所以我的代碼的其餘部分可以調用它,而不必實例化它首先。也許是這樣的:創建使用線程的靜態類的最佳方法是什麼?

internal static class Log 
{ 
    private static string _logFile = ""; 

    internal static void InitializeLogFile(string path) 
    { 
     ... 
    } 
    internal static void WriteHeader() 
    { 
     ... 
    } 
    internal static void WriteLine(params string[] items) 
    { 
     ... 
    } 
} 

現在,我想的內部旋轉了自己的線程,並以非同步的方式執行,可能使用BackgroundWorker的幫助簡化事情。我應該在每種方法中創建一個新的BackgroundWorker,創建一個靜態的BackgroundWorker作爲靜態類的私有屬性,或者是我可以忽略的東西嗎?

+2

在應用程序中使用非靜態類和單個靜態(單例)實例會更好。靜態類對於擴展方法是必需的,但最好避免。 – 2011-02-08 17:33:38

+0

是不是`BackgroundWorker`應該在WinForms中使用? – bzlm 2011-02-08 17:34:30

+1

@bzlm BackgroundWorker適用於.NET;不僅僅是WinForms – 2011-02-08 17:45:28

回答

0

良好的通話,

你一定要記錄操作能在一個單獨的線程中做記錄代碼發生。例如,當記錄器將事件記錄到文件時,訪問器方法(如「logEvent(myEvent)」)不應阻塞文件I/O操作。

創建一個隊列,以便訪問者只需將項目推送到隊列中。這樣,您的代碼在嘗試記錄事件時不應該阻塞。

啓動第二個線程來清空內部事件隊列。此線程可以在記錄器類的靜態私有方法上運行。

當您嘗試確保基礎事件隊列的線程安全時,會出現性能缺陷。每次在彈出或推入隊列之前,您都需要在隊列上獲取鎖定。

希望這會有所幫助。

0

我認爲我的建議是不正是你期待什麼,但我希望它是有用的呢:

  • 不要使用靜態類。相反, 使用一個普通的類,並保存它的一個 實例(singleton pattern);使用依賴關係 注塑引擎幫助很多 這(我使用MS Unity和它 工作正常)。如果你爲你的日誌類定義一個接口,你的代碼將更加可測試。
  • 至於線程的東西,如果我明白correclty你想要 日誌工作在 執行單獨的線程。你確定 你真的需要這個嗎?一個記錄器應該足夠簡單,以便您可以簡單地調用「寫入」方法和 ,期望您的應用程序 的性能不會受到影響。

最後一點:你提到了BackgroundWorker類,但是如果我沒有錯的話,這個類將用於桌面應用程序,而不是用於ASP.NET。在這種環境下,你應該使用類似the ThreadPool class的東西。

只是我的2歐分......

0

我自己創建了一個線程安全日誌類。我用過這樣的東西。

Logging obj = new Logging(filename); 
Action<string> log = obj.RequestLog(); 

RequestLog將返回寫給自己的隊列匿名方法。由於Q對於1個讀寫器來說是線程安全的,因此在調用log()時我不需要使用任何鎖()

實際的Logging對象將創建一個在後臺運行的新線程並定期檢查所有隊列。如果Q中有一個字符串,它會將它寫入緩衝的文件流。

我加一點額外的代碼來讀線程因此每個傳遞上的隊列提出,如果沒有什麼寫的,它會睡一個額外的10毫秒,高達100ms的最大值。這樣,線程不會打印太多。但是如果有大量的文字發生,它會每隔10ms查詢一次Q值。

下面是請求隊列中的返回碼的snpit。該「this.blNewData =真」是,所以我也沒必要打了每Q,看是否有新的數據寫入。不涉及鎖定,因爲假陽性仍然沒有工作,因爲所有Q都將是空的。

OutputQueue是我循環查看是否寫入任何內容的隊列列表。循環訪問列表的代碼在調用NewQueueLog()的情況下處於鎖定狀態,並導致列表調整大小。

public Action<String> NewQueueLog() 
{ 
    Queue<String> tmpQueue = new Queue<String>(32); 
    lock (OutputQueue) 
    { 
     OutputQueue.Add(tmpQueue); 
    } 
    return (String Output) => 
    { 
     tmpQueue.Enqueue(Output); 
     this.blNewData = true; 
    }; 
} 

最後,寫入日誌是無鎖的,這有助於大量線程寫入。

1

你只想每個日誌文件/ db有1個線程。否則,日誌中的項目順序不可靠。有一個後臺線程從一個線程安全的隊列中拉出並寫入。

2

您絕對不想在每次調用方法時啓用新線程或BackgroundWorker。我會在這裏使用生產者 - 消費者模式。事實證明,這是Microsoft爲我們提供的類的一個常見模式,它極大地簡化了實現。這種方法的好處是:

  • 只有一個額外的線程需要
  • Log方法將異步語義
  • 日誌消息的時間排序被保留

這裏有一些代碼讓你開始。

internal static class Log 
{ 
    private static BlockingCollection<string> s_Queue = new BlockingCollection<string>(); 

    static Log() 
    { 
    var thread = new Thread(Run); 
    thread.IsBackground = true; 
    thread.Start(); 
    } 

    private static void Run() 
    { 
    while (true) 
    { 
     string line = s_Queue.Take(); 
     // Add code to append the line to the log here. 
    } 
    } 

    internal static void WriteLine(params string[] items) 
    { 
    foreach (string item in items) 
    { 
     s_Queue.Add(item); 
    } 
    } 
} 
相關問題