我玩这些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()
{
}
阅读上取消 (这是介绍在.NET 4.0中,是基本不变从那时起)和基于任务的异步模式 ,它提供了关于如何使用准则CancellationToken
与async
方法。
总之,你传递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;
}
或者,为了避免修改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
你也可以用很好的扩展方法从https://github.com/StephenCleary/AsyncEx并让它看起来那么简单:
await Task.WhenAny(task, source.Token.AsTask());
尚未覆盖的一种情况是如何处理异步方法的取消内。 你需要一些数据上传到服务中获得它来计算的东西,然后返回一些结果就拿一个简单的例子。
public async Task<Results> ProcessDataAsync(MyData data)
{
var client = await GetClientAsync();
await client.UploadDataAsync(data);
await client.CalculateAsync();
return await client.GetResultsAsync();
}
如果你想支持取消那么最简单的方法是在令牌传递并检查它是否已经各自异步方法调用(或使用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;
}
}
因此,要使用那么它只需添加.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;
}
}
我只想补充到已接受的答案。 我被困在这一点,但我会在处理事件完全不同的路线。 而不是运行的await,我完成处理程序添加到任务。
Comments.AsAsyncAction().Completed += new AsyncActionCompletedHandler(CommentLoadComplete);
当事件处理程序看起来像这样
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;
}
有了这条路,所有的处理都已经完成了你,当任务被取消,它只是触发事件处理程序,你可以看到,如果它被取消了那里。