C# 如何取消等待中的任务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10134310/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to cancel a Task in await?
提问by Carlo
I'm playing with these Windows 8 WinRT tasks, and I'm trying to cancel a task using the method below, and it works to some point. The CancelNotification method DOES get called, which makes you think the task was cancelled, but in the background the task keeps running, then after it's completed, the status of the Task is always completed and never cancelled. Is there a way to completely halt the task when it's cancelled?
我正在处理这些 Windows 8 WinRT 任务,并且我正在尝试使用以下方法取消任务,并且它在某种程度上起作用。CancelNotification 方法确实被调用了,这会让你认为任务被取消了,但在后台任务一直在运行,然后任务完成后,任务的状态总是完成并且永远不会被取消。有没有办法在取消任务时完全停止任务?
private async void TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.Token.Register(CancelNotification);
source.CancelAfter(TimeSpan.FromSeconds(1));
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token);
await task;
if (task.IsCompleted)
{
MessageDialog md = new MessageDialog(task.Result.ToString());
await md.ShowAsync();
}
else
{
MessageDialog md = new MessageDialog("Uncompleted");
await md.ShowAsync();
}
}
private int slowFunc(int a, int b)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
}
return a + b;
}
private void CancelNotification()
{
}
采纳答案by Stephen Cleary
Read up on Cancellation(which was introduced in .NET 4.0 and is largely unchanged since then) and the Task-Based Asynchronous Pattern, which provides guidelines on how to use CancellationTokenwith asyncmethods.
阅读上取消(这是在.NET 4.0中引入的,是基本不变从那时起)和基于任务的异步模式,它提供了关于如何使用的指导方针CancellationToken与async方法。
To summarize, you pass a CancellationTokeninto each method that supports cancellation, and that method must check it periodically.
总而言之,您将 a 传递给CancellationToken支持取消的每个方法,并且该方法必须定期检查它。
private async Task TryTask()
{
CancellationTokenSource source = new CancellationTokenSource();
source.CancelAfter(TimeSpan.FromSeconds(1));
Task<int> task = Task.Run(() => slowFunc(1, 2, source.Token), source.Token);
// (A canceled task will raise an exception when awaited).
await task;
}
private int slowFunc(int a, int b, CancellationToken cancellationToken)
{
string someString = string.Empty;
for (int i = 0; i < 200000; i++)
{
someString += "a";
if (i % 1000 == 0)
cancellationToken.ThrowIfCancellationRequested();
}
return a + b;
}
回答by Smeegs
I just want to add to the already accepted answer. I was stuck on this, but I was going a different route on handling the complete event. Rather than running await, I add a completed handler to the task.
我只想添加到已经接受的答案中。我被困在这个问题上,但我在处理完整事件方面采用了不同的方法。我没有运行 await,而是向任务添加了一个已完成的处理程序。
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
Where the event handler looks like this
事件处理程序看起来像这样
private void CommentLoadComplete(IAsyncAction sender, AsyncStatus status )
{
if (status == AsyncStatus.Canceled)
{
return;
}
CommentsItemsControl.ItemsSource = Comments.Result;
CommentScrollViewer.ScrollToVerticalOffset(0);
CommentScrollViewer.Visibility = Visibility.Visible;
CommentProgressRing.Visibility = Visibility.Collapsed;
}
With this route, all the handling is already done for you, when the task is cancelled it just triggers the event handler and you can see if it was cancelled there.
使用此路由,所有处理都已为您完成,当任务被取消时,它只会触发事件处理程序,您可以在那里查看它是否被取消。
回答by sonatique
Or, in order to avoid modifying slowFunc(say you don't have access to the source code for instance):
或者,为了避免修改slowFunc(例如,假设您无权访问源代码):
var source = new CancellationTokenSource(); //original code
source.Token.Register(CancelNotification); //original code
source.CancelAfter(TimeSpan.FromSeconds(1)); //original code
var completionSource = new TaskCompletionSource<object>(); //New code
source.Token.Register(() => completionSource.TrySetCanceled()); //New code
var task = Task<int>.Factory.StartNew(() => slowFunc(1, 2), source.Token); //original code
//original code: await task;
await Task.WhenAny(task, completionSource.Task); //New code
You can also use nice extension methods from https://github.com/StephenCleary/AsyncExand have it looks as simple as:
您还可以使用来自https://github.com/StephenCleary/AsyncEx 的不错的扩展方法,让它看起来像这样简单:
await Task.WhenAny(task, source.Token.AsTask());
回答by kjbartel
One case which hasn't been covered is how to handle cancellation inside of an async method. Take for example a simple case where you need to upload some data to a service get it to calculate something and then return some results.
一种尚未涉及的情况是如何在异步方法内处理取消。举个简单的例子,你需要将一些数据上传到服务,让它计算一些东西,然后返回一些结果。
public async Task<Results> ProcessDataAsync(MyData data)
{
var client = await GetClientAsync();
await client.UploadDataAsync(data);
await client.CalculateAsync();
return await client.GetResultsAsync();
}
If you want to support cancellation then the easiest way would be to pass in a token and check if it has been cancelled between each async method call (or using ContinueWith). If they are very long running calls though you could be waiting a while to cancel. I created a little helper method to instead fail as soon as canceled.
如果您想支持取消,那么最简单的方法是传入一个令牌并检查它是否在每个异步方法调用之间被取消(或使用 ContinueWith)。如果它们是长时间运行的呼叫,尽管您可能需要等待一段时间才能取消。我创建了一个小助手方法,一旦取消就失败。
public static class TaskExtensions
{
public static async Task<T> WaitOrCancel<T>(this Task<T> task, CancellationToken token)
{
token.ThrowIfCancellationRequested();
await Task.WhenAny(task, token.WhenCanceled());
token.ThrowIfCancellationRequested();
return await task;
}
public static Task WhenCanceled(this CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
So to use it then just add .WaitOrCancel(token)to any async call:
所以要使用它,只需添加.WaitOrCancel(token)到任何异步调用中:
public async Task<Results> ProcessDataAsync(MyData data, CancellationToken token)
{
Client client;
try
{
client = await GetClientAsync().WaitOrCancel(token);
await client.UploadDataAsync(data).WaitOrCancel(token);
await client.CalculateAsync().WaitOrCancel(token);
return await client.GetResultsAsync().WaitOrCancel(token);
}
catch (OperationCanceledException)
{
if (client != null)
await client.CancelAsync();
throw;
}
}
Note that this will not stop the Task you were waiting for and it will continue running. You'll need to use a different mechanism to stop it, such as the CancelAsynccall in the example, or better yet pass in the same CancellationTokento the Taskso that it can handle the cancellation eventually. Trying to abort the thread isn't recommended.
请注意,这不会停止您正在等待的任务,它会继续运行。您需要使用不同的机制来停止它,例如CancelAsync示例中的调用,或者更好的是将相同的机制传递CancellationToken给 ,Task以便它最终可以处理取消。不建议尝试中止线程。

