我正在尝试使用await / async轮询功能来测试使用async / await处理事务和的小应用程序。
我的测试设置:
[TestMethod]
public void TestProcessTimerOnly()
{
// this initializes and kicks off the polling
var tp = new TransactionProcessor();
try
{
Thread.Sleep(5000);
tp.CancelProcessing();
}
catch (Exception ex)
{
LogErrors(ref tp, ex);
}
finally
{
DisplayLog(tp);
}
}
[TestMethod]
public void TestProcessTimerOnlyForcedCancellation()
{
// this initializes and kicks off the polling
var tp = new TransactionProcessor(1);
try
{
Thread.Sleep(5000);
tp.CancelProcessing();
}
catch (Exception ex)
{
LogErrors(ref tp, ex);
}
finally
{
DisplayLog(tp);
}
}
我的代码(全部在一个班级中):
// Constructor
public TransactionProcessor(int? debugForcedCancellationDelay = null)
{
// >>>>>>>> Setup Cancellation <<<<<<<<
if(debugForcedCancellationDelay.IsEmpty() || debugForcedCancellationDelay.IsZeroOrLess())
_cancellationToken = new CancellationTokenSource();
else
_cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(debugForcedCancellationDelay.Value));
// >>>>>>>> End <<<<<<<<
// was:
// RepeatActionEvery(() => TestingLog.Add("Repeat Action Every 1 Second"), TimeSpan.FromSeconds(1), _cancellationToken.Token).Wait();
// corrected:
// _processTask is defined as a global field of type Task...
_processTask = RepeatActionEvery(() => TestingLog.Add("Repeat Action Every 1 Second"), TimeSpan.FromSeconds(1), _cancellationToken.Token); //.Wait();
}
// was:
//public void CancelProcessing()
// corrected:
public async Task CancelProcessing()
{
_cancellationToken.Cancel();
await _processTask;
}
public static async Task RepeatActionEvery(Action action, TimeSpan interval, CancellationToken cancellationToken)
{
while (true)
{
action();
var task = Task.Delay(interval, cancellationToken);
try { await task; }
catch (TaskCanceledException) { return; }
}
}
当我运行TestProcessTimerOnly()测试时,它会坐在那里直到我最终取消测试装备。
当我运行TestProcessTimerOnlyForcedCancellation()测试时,它的行为符合预期。
所以问题归结为:我是否正确使用_cancellationToken变量?在一个实例中,我使用超时参数初始化。在另一个例子中,我初始化它没有参数。我在这里做错了什么?
答案 0 :(得分:6)
您非常不鼓励使用"sync over async"。
您正在从RepeatActionEvery
获取任务,该任务仅在取消令牌取消时结束。但是你正在等待该任务的同步(阻止),这意味着你永远不会离开构造函数并到达取消令牌的行(tp.CancelProcessing();
)。
当然,当您使用超时创建CancellationTokenSource
时,它将最终取消自己而不必调用它,因此任务将结束,等待它的线程将可以自由地完成构造函数并调用tp.CancelProcessing();
你应该做的事情(IIUC)是存储交易任务而不等待它,只有当你取消(或完成)交易时才等待:
public TransactionProcessor(int? debugForcedCancellationDelay = null)
{
// ...
Task = RepeatActionEvery(
() => TestingLog.Add("Repeat Action Every 1 Second"),
TimeSpan.FromSeconds(1),
_cancellationToken.Token);
}
public async Task CancelProcessingAsync()
{
_cancellationToken.Cancel();
await Task;
}
答案 1 :(得分:-2)
原因是因为在第一个示例中,您已使用.Wait()阻止所有线程,因此实际上没有发生任何事情,因为它等待了。它在第二个示例中起作用的原因是因为您已将cancelToken设置为在1秒后取消。这个取消令牌部分的“等待”是在另一个线程上,所以它仍然可以运行。
使用.Wait或Thread.Sleep将阻止所有线程。由于你的所有逻辑都在同一个线程上,所以你被困住了。使用Task.Run()。在RepeatEveryAction(()=&gt;)调用周围的Wait()(不带.Wait())应该清除它,因为逻辑将被放到另一个线程上运行。