【发布时间】:2015-03-20 09:09:33
【问题描述】:
我必须从外部库中调用一个方法。我无法控制它,它会阻塞 UI 5 秒以上。这只是一种方法。因为我真的很喜欢 async/await,所以我的代码是这样的:
SoundSource = await Task.Run(() => CodecFactory.Instance.GetCodec(Path));
问题是它可以持续大约 30 秒并且用户想要取消它。我的第一个想法:在 GetAwaiter().OnCompleted 中设置一个 AutoResetEvent 并运行一个 while,它等待 AutoResetEvent 500 毫秒,然后检查布尔变量 _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 来修复它,但这确实很复杂,并且很容易以死板的形式结束。
是否有“取消”方法/任务的好方法(如果他们不支持,我不必使用任务)。我不需要中止它,如果它可以一直运行到结束,而不是仅仅处理结果,那就太好了。
【问题讨论】:
-
您是否了解过 TPL 的取消支持?文档填充关于如何取消任务的信息。
-
是的,当然,但这只有在您控制任务时才有效。
-
如果该操作被构建为支持合作取消,它只能阻止该操作继续执行。在没有这种支持的情况下,您可以在操作实际完成之前将任务简单地标记为已完成,这正是您正在尝试做的事情,并且 TPL 肯定可以处理。
标签: c# async-await task cancellation