2015-02-09 62 views
4

我有下面的代碼示例:取消等待本身

public static async Task Async() 
{ 
    CancellationTokenSource source = new CancellationTokenSource(); 
    source.CancelAfter(500); 
    Stopwatch sw = Stopwatch.StartNew(); 
    await RunThread(ExpensiveOperation, source.Token); 
    sw.Stop(); 
    Console.WriteLine(sw.Elapsed); 
} 

public static async Task RunThread(Action act, CancellationToken token) 
{ //modify this method to handle cancelling the token during the following await 
    await Task.Run(act); //Task.Run(act, token) doesn't help 
} 

public static void ExpensiveOperation() 
{ 
    Thread.Sleep(1000); //simulates CPU expensive operation 
} 

現在,我怎麼能修改RunThread方法真正停止被取消,等待已久的任務,通過註冊任務,之後那些那麼回事500毫秒,不等待ExpensiveOperation的實際完成?

回答

3

您應該通過令牌操作本身,並且從時間檢查時間:

public static async Task RunThread(Action<CancellationToken> act, CancellationToken token) 
{ 
    await Task.Run(() => act(token), token); 
} 

public static void ExpensiveOperation(CancellationToken token) 
{ 
    for (int i = 0; i < 10; i++) 
    { 
     token.ThrowIfCancellationRequested(); 
     Thread.Sleep(100); 
    } 
} 

也傳遞令牌Task.Run因此返回Task會知道它被取消而不是僅僅出現故障。

如果不能從ExpensiveOperation內取消(無論是不能更改的代碼,或者它實際上是一個異步操作,而不是一個同步),然後使用該WithCancellation擴展方法:

static Task WithCancellation(this Task task, CancellationToken cancellationToken) 
{ 
    return task.IsCompleted 
     ? task 
     : task.ContinueWith(
      completedTask => completedTask.GetAwaiter().GetResult(), 
      cancellationToken, 
      TaskContinuationOptions.ExecuteSynchronously, 
      TaskScheduler.Default); 
} 

public static async Task RunThread(Action act, CancellationToken token) 
{ 
    await Task.Run(act).WithCancellation(token); 
} 

請注意,這種方法實際上並不取消操作,它只是讓您的代碼流動就像它的行爲一樣。

+0

@downvoter,錯了什麼? – i3arnon 2015-02-09 21:25:56

+0

如果任務在task.IsCompleted時尚未完成,但是在task.ContinueWith之前,WithCancellation方法會遇到麻煩嗎? – IllidanS4 2015-02-09 21:38:26

+0

@ IllidanS4 no。你總是可以使用'ContinueWith'。檢查'IsCompleted'只是一個優化,不會因爲任務已完成而煩惱註冊。 – i3arnon 2015-02-09 21:40:46