您的位置:首页 > 其它

用3种方式来实现异步取消Task后向当前SynchronizationContext执行操作

2013-01-19 23:04 501 查看
.NET TPL拥有非常大的灵活性,你会发现同一个操作会有许多不同的实现方式。正如标题所讲,我们来看这样一个简答的操作:取消Task的执行,然后在当前SynchronizationContext中执行代码。当然一切操作都必须是异步的,因此不能使用Task.Wait这样的方法。

我想到的实现方法有三种。

首先是准备工作,先写一个方法用来执行一个可以取消的Task,之后的具体实现代码就直接调用这个方法:

//执行一个可以取消的Task

static Task NewCancellableTask(CancellationToken token)

{

return Task.Run(() =>

{

while (true)

{

System.Threading.Thread.Sleep(1000);

token.ThrowIfCancellationRequested();

}

});

}

接下来看三种具体实现。

目录

方法一:使用await

方法二:使用ContinueWith和TaskScheduler

方法三:使用CancellationToken.Register方法

返回目录

方法一:使用await

这必须是首选,C# 5.0的await隐藏了许多TPL中的逻辑,以至于可以使异步执行的代码从外表上看起来和同步执行没太大区别。如下代码:

var cts = new CancellationTokenSource();

//执行取消操作

cts.CancelAfter(1000);

try

{

await NewCancellableTask(cts.Token);

}

catch (OperationCanceledException)

{

//收集OperationCanceledException

MessageBox.Show("操作取消");

}

运行几秒钟后,会显示“操作取消”对话框。

返回目录

方法二:使用ContinueWith和TaskScheduler

第二种方法我使用Task.ContinueWith,并且使用:

TaskContinuationOptions.OnlyOnCanceled

来确保只有在Task被取消后,后续操作才会被执行。

由于没有用await,因此SynchronizationContext的执行需要我们自己来做,方法就是使用TaskScheduler.FromCurrentSynchronizationContext方法来返回一个从当前SynchronizationContext创建的TaskScheduler。

Task.ContinueWith有诸多重载,我们使用这一个:

Task<TResult> ContinueWith<TResult>(Func<Task, TResult> continuationFunction, CancellationTokencancellationToken, TaskContinuationOptions continuationOptions, TaskScheduler scheduler);

最终代码:

var cts = new CancellationTokenSource();

//执行取消操作

cts.CancelAfter(1000);

//ContinueWith

NewCancellableTask(cts.Token).ContinueWith(

t => MessageBox.Show("操作取消"),

CancellationToken.None,

TaskContinuationOptions.OnlyOnCanceled,

TaskScheduler.FromCurrentSynchronizationContext());

(ContinueWith的Task事实上返回一个Task<MessageBoxResult>)

同样,运行成功。

返回目录

方法三:使用CancellationToken.Register方法

CancellationToken类型有一个Register方法可以传入一个委托,当CancellationToken被取消后这个委托会被调用。更不可思议的是,它还有一个useSynchronizationContext参数来指定是否回调方法被执行在当前SynchronizationContext上,非常给力。当然Register方法还可以传递一个额外参数,本例不需要使用。

下图:CancellationToken.Register方法的参数:





那么使用这种方法的实现代码:

var cts = new CancellationTokenSource();

//注册操作

cts.Token.Register(() => MessageBox.Show("操作取消"), true);

//执行取消操作

cts.CancelAfter(1000);

//直接执行Task,不需要其他操作。

NewCancellableTask(cts.Token);

这种方法比较少见,不过看起来还不错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: