2016-11-15 69 views
0

我們有一些遺留代碼可以測試多個類的線程安全性。最近的硬件升級(從2核心到4核心)呈現隨機故障,但訪問列表<>中的項目時出現異常。單元測試測試線程安全性 - 對象不隨機提供

 [Test] 
     public void CheckThreadSafeInThreadPool() 
     { 
      Console.WriteLine("Initialised ThreadLocalDataContextStore..."); 
      var container = new ContextContainerTest(); 
      Console.WriteLine("Starting..."); 
      container.StartPool(); 
      while (container.ThreadNumber < 5) 
      { 
       Thread.Sleep(1000); 
      } 

      foreach (var message in container.Messages) 
      { 
       Console.WriteLine(message); 
       if (message.Contains("A supposedly new thread is able to see the old value")) 
       { 
        Assert.Fail("Thread leaked values - not thread safe"); 
       } 
      } 

      Console.WriteLine("Complete"); 

     } 




public class ContextContainerTest 
    { 
     private ThreadLocalDataContextStore store; 
     public int ThreadNumber; 
     public List<string> Messages; 

     public void StartPool() 
     { 
      Messages = new List<string>(); 

      store = new ThreadLocalDataContextStore(); 
      store.ClearContext(); 
      var msoContext = new MsoContext(); 
      msoContext.Principal = new GenericPrincipal(new GenericIdentity("0"), null); 
      store.StoreContext(msoContext); 

      for (var counter = 0; counter < 5; counter++) 
      { 
       Messages.Add(string.Format("Assigning work item {0}", counter)); 
       ThreadPool.QueueUserWorkItem(ExecuteMe, counter); 
      } 
     } 

     public void ExecuteMe(object input) 
     { 

      string hashCode = Thread.CurrentThread.GetHashCode().ToString(); 

      if (store.GetContext() == null || store.GetContext().Principal == null) 
      { 
       Messages.Add(string.Format("[{0}] A New Thread", hashCode)); 
       var msoContext = new MsoContext(); 
       msoContext.Principal = new GenericPrincipal(new GenericIdentity("2"), null); 
       store.StoreContext(msoContext); 
      } 
      else if (store.GetContext().Principal.Identity.Name == "1") 
      { 
       Messages.Add(string.Format("[{0}] Thread reused", hashCode)); 
      } 
      else 
      { 
       Messages.Add(string.Format("[{0}] A supposedly new thread is able to see the old value {1}" 
        , hashCode, store.GetContext().GetDiagnosticInformation())); 
      } 

      Messages.Add(string.Format("[{0}] Context at starting: {1}", hashCode, store.GetContext().GetDiagnosticInformation())); 
      store.GetContext().SetAsCurrent(new GenericPrincipal(new GenericIdentity("99"), null)); 
      Messages.Add(string.Format("[{0}] Context at End: {1}", hashCode, store.GetContext().GetDiagnosticInformation())); 
      store.GetContext().SetAsCurrent(new GenericPrincipal(new GenericIdentity("1"), null)); 

      Thread.Sleep(80); 
      ThreadNumber++; 
     } 


    } 

失敗是隨機的,並且發生在測試本身的以下代碼段;

 foreach (var message in container.Messages) 
     { 
      Console.WriteLine(message); 
      if (message.Contains("A supposedly new thread is able to see the old value")) 
      { 
       Assert.Fail("Thread leaked values - not thread safe"); 
      } 
     } 

了微妙的變化可以解決問題,但有人是瑣碎的,我們不應該需要做的是,爲什麼是空的消息,如果消息是不是和爲什麼它工作的大部分時間,而不是別人。

if (message != null && message.Contains("A supposedly new thread is able to see the old value")) 
{ 
} 

另一種解決方案是將列表更改爲線程安全,但這並不能解答爲什麼問題首先出現。

+1

它看起來不像一個單元測試 - 它並不簡單,不清楚,似乎只測試一件事情並且具有複雜的設置邏輯。將列表轉換爲線程安全類,或者在訪問列表時使用鎖 - 這可能是某種競爭條件。您已經花費了更多時間來輸入此問題,而不僅僅是更改代碼以在多線程環境中使用正確的數據結構。另外,只要刪除這個測試 - 它聞起來很糟糕。 – oleksii

+0

「另一個解決方案是將列表更改爲線程安全,但這並不能解釋爲什麼問題首先出現」 - 但問題是因爲List不是線程安全的,您需要什麼其他原因? – Evk

回答

1

List<T>如果您使用.Net 4及更高版本,則不是線程安全元素,您可以使用ConcurrentBag<T>System.Collection.Concurrent,如果年齡較大,則需要自行實施。請參閱this可能會有所幫助。

希望我有幫助。