2013-07-18 56 views
0

所以,這個例子試圖給出一個我試圖修改的更大的系統(即Orchard CMS)的簡單視圖。因此,它可能不完美。循環依賴與橫切關注


我想創建一個通過設置管理的日誌記錄系統。我遇到的問題是檢索設置會導致記錄發生。下面是希望能說明問題一個簡單的例子:

static void Main(string[] args) 
{ 
    string[] messages = "this is a test. but it's going to be an issue!".Split(' '); 

    Parallel.ForEach(messages, Log); 

    Console.ReadLine(); 
} 

public static void Log(string message) 
{ 
    Console.WriteLine(GetPrefix() + message); 
} 

public static string GetPrefix() 
{ 
    Log("Getting prefix!"); 
    return "Prefix: "; 
} 

這是一個明顯的StackOverflowException。但是,我該如何解決它?我不能只是禁用日誌記錄,直到我得到GetPrefix的響應,因爲我可能會錯過日誌。 (事實上​​,在這個簡單的例子,我想念所有,但第一個。)

static void Main(string[] args) 
{ 
    string[] messages = "this is a test. but it's going to be an issue!".Split(' '); 

    Parallel.ForEach(messages, Log); 

    Console.ReadLine(); 
} 

static bool _disable = false; 
public static void Log(string message) 
{ 
    if (_disable) 
    { 
     return; 
    } 
    _disable = true; 
    Console.WriteLine(GetPrefix() + message); 
    _disable = false; 
} 

public static string GetPrefix() 
{ 
    Log("Getting prefix!"); 
    return "Prefix: "; 
} 

(^壞。)

注意,我目前沒有在GetPrefix方法控制,只有Log方法。

我不確定是否有辦法解決這個問題;我可能需要將其他設置(如配置或單獨的設置文件)。然而,如果任何人有想法或建議,我會很樂意嘗試任何事情,因爲我寧願現在離開設置(這是在管理界面中)。

+0

你必須同步對'bool _disable'的訪問。你失去了並行執行,這在這個例子中很好,因爲無論如何,Console.WriteLine是同步的。 – JosephHirn

+0

@Ginosaji:這是一個簡單的例子,但實際上,同步會對性能產生嚴重影響。 – zimdanen

+0

您仍然可以並行處理這些消息並只是同步日誌記錄。正如我所說,無論如何,Console.WriteLine是同步的,所以你通過鎖定'bool _disable'沒有任何損失。 – JosephHirn

回答

0

如何拆分日誌方法爲:

public static void LogWithPrefix(string message) 
{ 
    var prefix = GetPrefix(); 
    Log(prefix + message); 
} 

public static void Log(string message) 
{ 
    Console.WriteLine(message); 
} 
+0

前綴是必需的。在這種情況下,獲取前綴代表獲取日誌設置。沒有這些,我不知道在哪裏登錄。 – zimdanen

+0

@zimdanen:那麼,Log是什麼?(「Getting prefix!」);'應該登錄到原始示例中? – JayC

+0

@JayC:我想禁用它,所以它不記錄任何地方。 – zimdanen

1

所有你需要做的是禁用當前堆棧幀。現在你可以使用反射來遍歷堆棧框架,看看它是否被調用,但有一個更簡單的方法。每個線程都有一個堆棧框架。因此,使靜態變量[ThreadStatic]

[ThreadStatic] static bool _disable = false;

這是如何工作的?

http://msdn.microsoft.com/en-us/library/system.threadstaticattribute.aspx

「指示靜態字段的值對於每個線程是唯一的。」

編輯:但僅此可能不夠。你想要的是什麼可能是每個任務的一個靜態變量。現在,由於任務將按照每個線程順序執行,在這種特殊情況下,我認爲這不是問題,除非記錄器可能會在未禁用的情況下失敗......並且我不確定在這種情況下會發生什麼情況,但它可能需要你在一個try/finally塊最起碼換東西:

static void Main() //Main(string[] args) 
{ 
    string[] messages = "this is a test. but it's going to be an issue!".Split(' '); 

    Parallel.ForEach(messages, Log); 

    Console.ReadLine(); 
} 
[ThreadStatic] 
static bool _disable = false; 
public static void Log(string message) 
{ 
    if (_disable) 
    { 
     return; 
    } 
    try { 
     _disable = true; 
     Console.WriteLine(GetPrefix() + message); 
    } finally { 
     _disable = false; 
    } 
} 

public static string GetPrefix() 
{ 
    Log("Getting prefix!"); 
    return "Prefix: "; 
} 

編輯II:從http://msdn.microsoft.com/en-us/library/dd460712.aspx看來,一旦任何一組任務的罰球任務委託以外的異常,你不能保證執行任何剩餘的任務。最好在你的代表中處理這些特殊情況。

+0

+1:這適用於人爲的例子。看起來也有[.NET 4.0中的實例字段的等價形式](http://theburningmonk.com/2010/10/threadstatic-vs-threadlocal/)。我會玩這個,看看它的表現如何,並回饋反饋。謝謝! – zimdanen