如何在取消异步任务时同步等待

时间:2015-02-18 09:31:46

标签: c# .net multithreading asynchronous async-await

我有一个泛型类,它以异步方式运行某些工作包。它可以取消所有任务的执行,并在完成取消的任务时同步等待。在新事务(用户执行某些操作)启动之前触发取消。这是必要的,因为异步任务以及新事务会改变相同的对象,但异步任务会在假定事务之前的状态时执行它。

以下为此行为如此重要的示例代码:

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功能来实现此行为?

2 个答案:

答案 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}}上有更详细的描述。