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

 

.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

这必须是首选,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方法的参数:

image

 

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

var cts = new CancellationTokenSource();

//注册操作

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

//执行取消操作

cts.CancelAfter(1000);

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

NewCancellableTask(cts.Token);

 

这种方法比较少见,不过看起来还不错。

作者:Mgen

本文版权归作者所有,欢迎以网址(链接)的方式转载,不欢迎复制文章内容的方式转载,其一是为了在搜索引擎中去掉重复文章内容,其二复制后的文章往往没有提供本博客的页面格式和链接,造成文章可读性很差。望有素质人自觉遵守上述建议。

如果一定要以复制文章内容的方式转载,必须在文章开头标明作者信息和原文章链接地址。否则保留追究法律责任的权利。

 

 
 
标签: BCLThreadingTPLAsynchrony
posted on 2013-01-19 23:04  HackerVirus  阅读(725)  评论(0编辑  收藏  举报