2011-07-07 110 views
8

我最近有在測試,這是類似以下的面試問題,我沒有使用線程可以有人幫告訴我如何發展的非常多的經驗可以解決這個問題?:線程測試問題

public class StringQueue 
{ 
    private object _lockObject = new object(); 

    private List<string> _items = new List<string>(); 

    public bool IsEmpty() 
    { 
     lock (_lockObject) 
      return _items.Count == 0; 
    } 

    public void Enqueue(string item) 
    { 
     lock (_lockObject) 
      _items.Add(item); 
    } 

    public string Dequeue() 
    { 
     lock (_lockObject) 
     { 
      string result = _items[0]; 
      _items.RemoveAt(0); 

      return result; 
     } 
    } 
} 

下面的方法在上面的實現中是線程安全的,爲什麼?

public string DequeueOrNull() 
{ 
    if (IsEmpty()) 
     return null; 

    return Dequeue(); 
} 
+1

我標記此C#,因爲它看起來像。如果我錯了,請糾正我。 –

回答

9

在我看來,答案是否定的。當一個isEmpty()過程鎖定對象時,一旦返回調用就會被釋放 - 不同的線程可能會在調用IsEmpty()和Dequeue()之間調用DequeueOrNull()(此時對象被解鎖),從而刪除存在的唯一項目,使Dequeue()在當時無效。

可能的修復方法是將鎖定置於DequeueOrNull()中的兩個語句上,因此在檢查之後但DeQueue()之前沒有其他線程可以調用DeQueue()。

+0

如果你寫的補丁已經到位了,'把鎖放在DequeueOrNull()中的兩個語句中「,那麼這些鎖在單獨的函數中是否需要? – bitbucket

+0

@bitbucket:如果你把這些鎖取出來,所有的線程安全保證會被破壞,因爲它們是公共方法,如果它們在DequeueOrNull的中間被調用,他們不會在乎是否鎖定並且踐踏事物 –

+0

我想你需要鎖定一個不同的對象,否則可能是一個死鎖,但也許我錯了,我不知道當鎖語句嵌套時會發生什麼...... –

2

不,因爲_items的狀態可能會在線程安全的IsEmpty()和線程安全的Dequeue()調用之間發生潛在變化。

public string DequeueOrNull() 
{ 
    lock (_lockObject) 
    { 
    if (IsEmpty()) 
     return null; 

    return Dequeue(); 
    } 
} 

注:

與類似於以下,從而確保_items在整個操作過程中鎖定修復它取決於在_lockimplementation,你可能希望避免通過雙鎖定資源將IsEmpty()Dequeue的內容移入單獨的幫助函數。

+2

'lock'語句調用可重入的'Monitor.Enter'。 –

+1

@Martinho a h,我明白了,謝謝。當我回答這個問題時,它沒有被標記爲C#(雖然我認爲它是,我認爲面試問題傾向於語言不可知)。我認爲信號量/互斥概念值得提出,概念比語言特定的實現更重要。 –

3

它不是線程安全的。在標線有可能是在出列方法是從另一個線程調用,因此,隨之而來的出列返回錯誤值:

public string DequeueOrNull() 
{ 
    if (IsEmpty()) 
     return null; 
/// << it is possible that the Dequeue is called from another thread here. 
    return Dequeue(); 
} 

線程安全的代碼是:

public string DequeueOrNull() 
{ 
    lock(_lockObject) { 
    if (IsEmpty()) 
     return null; 
    return Dequeue(); 
    } 
}