2012-04-13 2 views
104

Я играю с этими задачами 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() 
{ 
} 

ответ

161

Читайте на Cancellation (который был введен в .NET 4.0 и в значительной степени неизменным с тех пор) и Task-Based Asynchronous Pattern, в котором содержатся рекомендации о том, как использовать 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; 
} 
+2

Wow great info! Это сработало отлично, теперь мне нужно выяснить, как обрабатывать исключение в методе async. Спасибо чувак! Я прочитаю материал, который вы предложили. – Carlo

+1

Удобное обращение было легко. Еще раз спасибо! =) – Carlo

+0

Эй, парень, есть ли способ сделать это, если у меня нет доступа к медленному методу? Например, предположим, что slowFunc был в черном ящике, и у вас есть доступ только к вызову метода, но не для изменения чего-либо в нем? – Carlo

5

Я просто хочу добавить к уже принятому ответу. Я застрял на этом, но я пошел по другому пути обработки полного события. Вместо того, чтобы ждать, я добавлю обработчик к задаче.

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; 
} 

С этим маршрутом, все обращение уже сделано для вас, когда задача будет отменена она просто вызывает обработчик событий, и вы можете увидеть, если он был отменен там.

20

Или для того, чтобы избежать изменения 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()); 
+0

Отличный ответ, спасибо. – Elton

+0

Это выглядит очень сложным ... как реализация async-ожидания. Я не думаю, что такие конструкции делают исходный код более удобочитаемым. – Maxim

Смежные вопросы