使用取消支持运行方法异步

时间:2015-01-21 18:22:35

标签: c# async-await task cancellation

我必须从外部库调用一个方法。我无法控制它,它会阻止用户界面5秒+。这只是一种方法。因为我非常喜欢async / await,我的代码看起来像这样:

SoundSource = await Task.Run(() => CodecFactory.Instance.GetCodec(Path));

问题是它可以持续大约30秒,并且用户想要取消它。我的第一个想法:设置GetAwaiter().OnCompleted一个AutoResetEvent并运行一个while等待500秒的AutoResetEvent,然后检查,如果bool变量_cancelLoading为真:

            using (var waithandler = new AutoResetEvent(false))
            {
                var x = track.GetSoundSource();
                x.GetAwaiter().OnCompleted(() => { waithandler.Set(); });
                while (!x.IsCompleted)
                {
                    await Task.Run(() => waithandler.WaitOne(500));
                    if (_cancelLoading)
                    {
                        x.GetAwaiter().OnCompleted(() => x.Result.Dispose());
                        _isLoadingSoundSource = false;
                        _cancelLoading = false;
                        return;
                    }
                }
                _isLoadingSoundSource = false;
                SoundSource = x.Result;
            }

但这不是一个好的解决方案,因为它不知道它是否可以将_isLoadingSoundSource设置为true,因为新方法仍然可以运行该过程。它可以用另一个全局AutoResetEvent修复,但这真的很复杂,最终可以很容易地结束。

是否有一种很好的方式来取消"方法/任务(如果他们不支持,我不必使用任务)。我不需要中止它,如果它可以运行直到它结束并且只是处理结果将会很好。

1 个答案:

答案 0 :(得分:2)

如果您无法取消实际操作(GetCodec),那么当您请求取消时,您只能忽略结果

private CancellationTokenSource _cts;
async Task SetSoundSourceAsync(string path)
{
  if (_cts != null)
    _cts.Cancel();
  _cts = new CancellationTokenSource();
  var token = _cts.Token;
  var result = await Task.Run(() => CodecFactory.Instance.GetCodec(path));
  if (token.IsCancellationRequested)
    return;
  SoundSource = result;
}

上面的示例代码假定它是从UI线程调用的。如果不是,则_cts会出现竞争条件。