[英]Two methods which can't run concurrently, how to write unit tests for this case, or make it testable?
Let's say I have two methods in my class MethodA
and MethodB
. 假设我在类MethodA
和MethodB
有两个方法。 Those methods are run in async, and I don't want that they run at the same time. 这些方法是异步运行的,我不希望它们同时运行。 In the app, MethodA
is called by the user, but MethodB
is run by a Timer in a background thread. 在应用程序中, MethodA
由用户调用,而MethodB
由计时器在后台线程中运行。
How I see the implementation in C#ish pseudocode: 我如何看待C#ish伪代码中的实现:
class MyClass
{
private static object _lock = new object();
public async Task MethodA()
{
lock(_lock)
{
await DoSomeStuff();
}
}
public async Task MethodB()
{
if(Monitor.IsEntered(_lock)
return;
lock(_lock)
{
await DoSomeStuff();
}
}
}
So first question is - is above approach correct? 所以第一个问题是-以上方法正确吗? I guess that's more a question for https://codereview.stackexchange.com/ . 我想这是https://codereview.stackexchange.com/的另一个问题。
So the second questions is - assuming that the approach is correct, how I can unit test, that MethodA
waits for MethodB
, and that MethodB
doesn't run when MethodA
is running? 因此,第二个问题是-假设的做法是正确的,我怎么能单元测试,即MethodA
等待MethodB
,而MethodB
时不运行MethodA
运行? Or how can I refactor it so that's testable? 还是我可以重构它以便进行测试?
EDIT: accordingly to comments, changed from using flags to a lock. 编辑:根据注释,从使用标志更改为锁定。
Boolean flags are the obsolete way to synchronize two threads. 布尔标志是使两个线程同步的过时方法。 It causes race conditions when one thread can read a value of false
while other thread is updating the value to true
; 当一个线程可以读取false
值而另一个线程将其值更新为true
时,则会导致竞争。
Since your case it not straightforward (B shouldn't way for B to end, while A should wait), then I would change the class use a Semaphore
like this: 由于您的情况并不简单(B不应结束B,而A应该等待),因此我将使用Semaphore
来更改类,如下所示:
public class MyClass
{
private SemaphoreSlim semaphore = new SemaphoreSlim(1);
public async Task MethodA()
{
await semaphore.WaitAsync();
await DoSomeStuff();
semaphore.Release();
}
public async Task MethodB()
{
bool success = await semaphore.WaitAsync(1);
if (!success)
return;
await DoSomeStuff();
await Task.Delay(TimeSpan.FromSeconds(5));
semaphore.Release();
}
}
I would consider putting all that in try..catch..finally
block and release the semaphore in the finally block, but i'm trying to keep it simple while you can add that yourself. 我会考虑将所有内容都放入try..catch..finally
块中,并在finally块中释放该信号量,但是我试图将其简化,而您可以自己添加。
This is not straight forward to test. 这不是直接测试。 Taking threads into account, you might need to repeat the test multiple times to reach a case of failure. 考虑到线程,您可能需要多次重复测试才能达到失败的情况。 You might need to introduce an overload for method A that waits for some times, which might prove that method B is waiting for it. 您可能需要为方法A引入等待一段时间的重载,这可能证明方法B正在等待它。 Here is the test. 这是测试。 To test the case of failure, change new SemaphoreSlim(1);
要测试失败的情况,请更改new SemaphoreSlim(1);
to new SemaphoreSlim(2);
到new SemaphoreSlim(2);
and the test would fail because MethodB
would start before MethodA
ends. 并且测试将失败,因为MethodB
将在MethodA
结束之前开始。
[TestMethod]
public async Task TestingMyClassThreadSync()
{
int repeatTimes = 100;
int counter = 0;
while (counter++ <= repeatTimes)
{
MyClass myClass = new MyClass();
Task tA = myClass.MethodA();
Task tB = myClass.MethodB();
Task finishedTask = await Task.WhenAny(tA, tB);
bool bFinishedBeforeA = finishedTask == tA;
if (bFinishedBeforeA)
Assert.Fail();
}
}
I would introduce an overload: 我会介绍一个重载:
public async Task MethodA(long waitMilliseconds = 0)
{
await semaphore.WaitAsync();
await DoSomeStuff();
await Task.Delay(waitMilliseconds);
semaphore.Release();
}
Then Call it from unit testing as MethodA(5000);
然后从单元测试中将其称为MethodA(5000);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.