2016-05-25 33 views
4

我在遇到某種情況時遇到了自己,當我重寫的ToString()方法將使用其中一個實例的集合時。在ToString()方法中使用lock()進行集合

public override string ToString() 
{ 
    string returnStr = "testString"; 

    lock (_sync) 
    { 
     returnStr = $"{returnStr}: {string.Join(",", _testList)}"; // where _testList is a List<string> in the class's scope 
    } 

    return returnStr; 
} 

因爲我們正在談論的是一個多線程應用程序,我想知道,因爲我會爲了避免集合的修改使用lock()ToString()方法的身體內部產生我返回的字符串的同時。雖然我正在對集合進行修改,但當然,同一對象(_sync)正在被鎖定。
但是,這個方法也被其他幾個進程使用,比如Visual Studio的調試器,誰知道還有什麼,因爲這個方法是繼承自Object類本身。
例如:我擔心它可能會在場景後面(通過框架或其他任何東西)被更頻繁地調用,並且必須使用鎖定(這可能會導致性能下降),或者可能導致死鎖我在不好的時刻進行調試。

問:
我要關心這個情況,因爲這可能會導致問題,或者是確定以使用對象內ToString()鎖定(以及從.NET類型其他繼承的方法)?

有沒有更好的替代解決方案來實現這個目標?

注意:我正在考慮在每次修改時(在鎖的內部,它被操作的地方)從集合中生成所需的字符串,所以我會將集合的字符串格式準備好連接起來在ToString()方法本身。我想這對性能會更好,因爲string.Join()的過程不會在每次呼叫ToString()時都運行,但我真的很想知道更多關於這種情況的信息。

謝謝!

回答

1

關於線程安全,ToString沒有特別的規定。線程安全是您程序的全局屬性。您必須理解和控制同時訪問數據的所有內容。

如果在訪問數據時可能會同時發生ToString,則必須進行適當的同步。在存在併發寫入的情況下從List讀取特別不安全。

由於ToString中的關鍵區域只使用一個鎖而不會阻塞,因此死鎖的風險很小。一旦進入鎖定,它將最終保證退出。這對鎖定來說是一個很好的設計原則。

也許你可以使用無鎖模式,如不可變集合?或者,也許你可以讓你的整個對象不可變。

1

在這種情況下,您不必擔心死鎖。死鎖需要至少2個鎖,而你只有一個鎖。 List類本身不使用任何鎖,也不使用string.Join或string.Format。

不管它是否是性能問題完全取決於您提及的其他應用程序如何使用它。 Visual Studio調試器不會經常調用ToString,只有在調試器中暫停時纔會調用ToString!這不會是一個問題。

至於你提到的其他應用程序,你只會通過分析知道。

1

你真的需要/經常使用ToString()嗎? 您的場景似乎更像是一種業務邏輯,我不會將它與ToString()實現混在一起。

我會去是這樣的:GetSnapshotRepresentation()使用鎖和整個的難題,並留下ToString()返回的東西非常輕巧(如編號,類型,一成不變的東西,等等)

把鎖和重型機械到ToString()如果您打電話給ToString(),您不希望/不需要做的事,您將阻止您的作者。

0

你不需要擔心死鎖,因爲@DavidJ說,它們不會發生只有一個鎖。但是,您應該關注race conditions

讓我們假設除了ToString方法之外,還有一種方法可以刪除列表中的其他所有項目。

for(int i = _testList.Count - 1; i >= 0; i -= 2) 
{ 
    _testList.RemoveAt(i); 
} 

由於您的應用程序是多線程的,你的ToString()可以,而另一個線程在這個循環的中間調用。你可能會認爲你需要鎖定所有訪問列表如下:

for(int i = _testList.Count - 1; i >= 0; i -= 2) 
{ 
    lock (_sync) { 
     _testList.RemoveAt(i); 
    } 
} 

的這裏的問題是:假設你的循環已經重複了幾次,但在完成前被中斷。如果中斷循環的線程調用ToString(),則ToString()方法將輸出一個處於不一致狀態的列表。從其他線程訪問你的列表不應該發生在循環內部。你會實際上有把鎖這樣的循環之外:

lock(_sync) { 
    for(int i = _testList.Count - 1; i >= 0; i -= 2) 
    { 
     _testList.RemoveAt(i); 
    } 
} 

我想在這裏說明的是,有對多線程代碼沒有簡單的規則。只要代碼的其他部分也是線程安全的,您的ToString()方法就是線程安全的。