我有一个带有静态ConcurrentQueue的类。一个类接收消息并将其放入队列,而该类上的另一个线程则从该队列读取消息并一次处理它们。该方法将被取消,并带有取消令牌。
清空队列的方法如下:
public async Task HandleEventsFromQueueAsync(CancellationToken ct, int pollDelay = 25)
{
while (true)
{
if (ct.IsCancellationRequested)
{
return;
}
if(messageQueue.TryDequeue(out ConsumeContext newMessage))
{
handler.Handle(newMessage);
}
try
{
await Task.Delay(pollDelay, ct).ConfigureAwait(true);
}
catch (TaskCanceledException)
{
return;
}
}
}
我的测试方法如下:
CancellationToken ct = source.Token;
Thread thread = new Thread(async () => await sut.HandleEventsFromQueueAsync(ct));
thread.Start();
EventListener.messageQueue.Enqueue(message1);
EventListener.messageQueue.Enqueue(message2);
await Task.Delay(1000);
source.Cancel(false);
mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2));
因此,我使用新的取消令牌在其自己的线程中启动出队方法。然后,我排队了几条消息,给进程第二秒钟处理它们,然后使用source.Cancel(false)终止线程并使方法返回。然后,我检查处理程序是否被调用了正确的次数。当然,我会进行多种测试,中止出队方法的消息类型和时间不同。
问题在于,当我分别运行任何测试时,它们都将成功。但是,当我尝试将它们作为一个组运行时,Visual Studio不会运行每个测试。没有错误消息,并且确实可以成功进行测试,但是运行仅在第二次测试后停止。
我不知道为什么会这样。我的测试在结构上都是相同的。我每次都正确地中止出队线程。
什么可以迫使Visual Studio停止测试运行而又不会引发任何类型的错误?
答案 0 :(得分:1)
我已经解决了自己的问题。事实证明,新创建的线程引发了异常,当线程引发异常时,这些异常将被忽略,但它们仍然阻止单元测试的进行。解决导致异常的问题后,测试可以正常进行。
答案 1 :(得分:1)
您正在将异步lambda传递给Uncaught TypeError: Cannot read property 'cod3nFilter' of undefined thrown
Uncaught TypeError: Cannot read property '0' of undefined thrown
Uncaught TypeError: Cannot read property 'length' of undefined thrown
构造函数。 Thread
构造函数无法理解异步委托(不接受Thread
参数),因此最终得到Func<Task>
lambda。异步void方法should be avoided用于不是事件处理程序的任何事情。您遇到的情况是,当代码到达第一个async void
时,显式创建的线程终止,其余主体在await
线程中运行。似乎代码永远不会因异常而失败,否则进程将崩溃(这是异步void方法的默认行为)。
建议:
ThreadPool
代替Task
。这样一来,您在退出测试之前将需要Thread
做些事情。await
考虑使用BlockingCollection
或类似Channel
的异步队列代替CancellationToken ct = source.Token;
Task consumerTask = Task.Run(() => sut.HandleEventsFromQueueAsync(ct));
EventListener.messageQueue.Enqueue(message1);
EventListener.messageQueue.Enqueue(message2);
await Task.Delay(1000);
source.Cancel(false);
await consumerTask; // Wait the task to complete
mockedHandler.Verify(x => x.Handle(It.IsAny<ConsumeContext>()), Times.Exactly(2));
。轮询是一种笨拙且效率低下的技术。使用阻塞或异步队列,您将不必执行循环来等待新消息到达。您将能够进入ConcurrentQueue
状态,并在收到新消息时立即得到通知。
使用waiting
配置等待。 ConfigureAwait(true)
是默认设置,不执行任何操作。
考虑通过抛出ConfigureAwait(false)
来传播取消。这是.NET中传播取消的standard way。所以代替:
OperationCanceledException
...最好这样做:
if (ct.IsCancellationRequested) return;