2017-03-31 70 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."); 
    }