2017-03-31 111 views
0

我正在研究多線程應用程序,並且有一部分代碼只能在一個線程中同時運行。沒什麼複雜。我使用鎖定來同步它。它在生活系統中工作,但我想編寫單元測試來檢查是否只有一個線程處於關鍵部分。我寫了一個,它正在工作,但它停止:) 我不知道如何編寫一個測試它以適當的方式。我使用NSubstitute來創建模擬。線程同步(鎖)的單元測試

類來測試:

public interface IMultiThreadClass 
{ 
    void Go(); 
} 
public class Lock02 : IMultiThreadClass 
{ 
    private readonly IProcessor _processor; 
    private readonly string _threadName; 

    private static readonly object Locker = new Object(); 

    public Lock02(IProcessor processor, string threadName) 
    { 
     _processor = processor; 
     _threadName = threadName; 
    } 

    public void Go() 
    { 
     //critical section 
     lock (Locker) 
     { 
      _processor.Process(_threadName); 
     } 
    } 
} 

測試:

[TestMethod()] 
public void Run_Test() 
{ 
    //Only one thread should run Processor.Process, but we allow max 2 threads to catch locking erorrs 
    SemaphoreSlim semaphore = new SemaphoreSlim(1, 2); 

    //Semaphore to synchronize asserts 
    SemaphoreSlim synchroSemaphore = new SemaphoreSlim(0, 1); 

    IProcessor procesor = Substitute.For<IProcessor>(); 
    procesor.When(x => x.Process(Arg.Any<string>())).Do(y => 
    { 
     //increment counter to check if method was called 
     Interlocked.Increment(ref _counter); 

     //release synchro semaphore 
     synchroSemaphore.Release(); 

     //stop thread and wait for release 
     semaphore.Wait(); 
    }); 

    Lock02 locker1 = new Lock02(procesor, "1"); 
    Lock02 locker2 = new Lock02(procesor, "2"); 
    Lock02 locker3 = new Lock02(procesor, "3"); 

    Task.Run(() => locker1.Go()); 
    Task.Run(() => locker2.Go()); 
    Task.Run(() => locker3.Go()); 

    //ASSERT 
    //Thread.Sleep(1000); 
    synchroSemaphore.Wait(); 
    Assert.AreEqual(1, _counter); 

    semaphore.Release(1); 
    synchroSemaphore.Wait(); 
    Assert.AreEqual(2, _counter); 

    semaphore.Release(1); 
    synchroSemaphore.Wait(); 
    Assert.AreEqual(3, _counter); 

    semaphore.Release(1); 
} 

回答

1

一種可能的(簡單但不防彈)的方法是產卵某些線程/任務在單元測試中,每個提取和臨時存儲一個int變量(可能是靜態的),等待一個位(延遲),遞增該值並將其寫回變量。如果沒有線程同步(鎖定),很多(如果不是全部)線程將獲取相同的數字,並且它不會與線程/任務數量相等(應該如此)。

這是不是防彈,因爲還有一個競爭條件使它不可再生(臭代碼是50毫秒的延遲),雖然它似乎(我)非常不可能對所有胎面等待每個其他以完美的方式產生正確的結果。

我認爲這是一個臭的解決方法,但它很簡單,有效。

[TestMethod] 
    public async Task APossibleTest() 
    { 
     int importantNumber = 0; 

     var proc = Substitute.For<IProcessor>(); 
     proc.WhenForAnyArgs(processor => processor.Process(Arg.Any<string>())) 
      .Do(callInfo => 
      { 
       int cached = importantNumber; 
       // Wait for other threads to fetch the number too (if they were not synchronized). 
       Thread.Sleep(TimeSpan.FromMilliseconds(50)); 
       // This kind of incrementation will check the thread synchronization. 
       // Using a thread-safe Interlocked or other here does not make sense. 
       importantNumber = cached + 1; 
      }); 

     var locker = new Locker(proc, "da horror"); 

     // Create 10 tasks all attempting to increment the important number. 
     Task[] tasks = 
      Enumerable 
       .Range(0, 10) 
       // You could create multiple lockers here (with their own processors). 
       .Select(i => Task.Run(() => locker.Go())) 
       .ToArray(); 
     await Task.WhenAll(tasks); 

     Assert.AreEqual(10, importantNumber, "Exactly 10 increments were expected since we have 10 tasks."); 
    }