我有一个泛型类,它以异步方式运行某些工作包。它可以取消所有任务的执行,并在完成取消的任务时同步等待。在新事务(用户执行某些操作)启动之前触发取消。这是必要的,因为异步任务以及新事务会改变相同的对象,但异步任务会在假定事务之前的状态时执行它。
以下为此行为如此重要的示例代码:
private void Transaction()
{
asyncExecution.AbortAllAsyncWork();
DoTransaction();
}
此方法被同步调用,DoTransaction更改在异步任务中也发生更改的对象。例如,列表可以在迭代时更改。
以前,我在传递同步任务调度程序的任务中使用ContinueWith方法实现了此行为。总而言之,它很难理解,看起来有点脏。因此,我想知道我是否可以使用新的async-await功能实现相同的行为。这里的问题在于here描述的死锁。到目前为止,代码遇到了死锁问题:
public void RunAsync<TWork, TResult>(IIncrementalAsyncExecutable<TWork, TResult> executable, TWork initialWork) where TWork : class
{
Task workingTask = RunAsyncInternal(executable, initialWork, CancellationTokenSource);
if (IsRunning(workingTask))
{
workingTasks.Add(workingTask);
}
}
private async Task RunAsyncInternal<TWork, TResult>(IIncrementalAsyncExecutable<TWork, TResult> executable, TWork initialWork, CancellationTokenSource tokenSource) where TWork : class
{
while (!executable.WorkDone)
{
TResult result = await Task.Run(() => executable.CalculateNextStep(initialWork));
executable.SyncResult(result);
if (tokenSource.IsCancellationRequested)
{
return;
}
}
}
public void AbortAllAsyncWork()
{
CancellationTokenSource.Cancel();
foreach (Task workingTask in workingTasks)
{
if (IsRunning(workingTask))
{
workingTask.Wait(); // here is the deadlock problem
}
}
}
是否有可能使用没有死锁的新async-await功能来实现此行为?
答案 0 :(得分:3)
如果要取消每个Task
,您只需await
就可以OperationCanceledException
抓住public async Task AbortAllAsyncWork()
{
CancellationTokenSource.Cancel();
foreach (Task workingTask in workingTasks.Keys)
{
if (IsRunning(workingTask))
{
try
{
await workingTask;
}
catch (OperationCanceledException oce)
{
// Do something usefull
}
}
}
}
:
await Task.WhenAll
或者,你可以简单await Task.WhenAll(workingTasks.Keys)
完成所有任务:
Keys
假设IEnumerable<Task>
是Task
,则返回的Canceled
将处于{{1}}状态。
答案 1 :(得分:3)
我有一个泛型类,它以异步方式运行某些工作包。
使用内置类型非常容易,比如TPL Dataflow。他们已经完成了所有艰苦的工作。
问题是,应用程序是同步设计的。
是的,但请注意问题出在应用程序的设计中。等待任务完成是一种固有的异步操作,最佳解决方案肯定是Yuval's。
以前,我在传递同步任务调度程序的任务中使用ContinueWith方法实现了此行为。
我不知道这怎么可能避免你所看到的僵局。
我想找到一个解决方案,不需要重构。
你真正要问的是“我如何进行同步异步”。 best answer is "don't"。Stephen Toub's blog。但是有一些黑客可以用它来强制它在某些情况下工作。 没有始终有效的通用解决方案。
这些hacks是:只是阻塞,将操作推送到后台线程,然后运行嵌套的消息循环。它们在{{3}}上有更详细的描述。