现在的位置: 首页 > 综合 > 正文

.NET4.0并行计算技术基础(9)

2013年10月01日 ⁄ 综合 ⁄ 共 4400字 ⁄ 字号 评论关闭

 

 

今天贴文,发现CSDN博客又出现了BUG,图片无法上传。所以文中插图就没有了。等CSDN博客恢复正常后再补上。

 

呵呵,我早已经习惯了CSDN博客时不时地闹点小脾气。

 

补充一句:CSDN博客开发小组的小伙子们,干活得加把劲啊,一个专业网站不应该经常出现这么明显的BUG,上线前请认真测试一下你们的程序,别偷懒。

 

金旭亮

2009.10.14

 

=============================================

 

 .NET4.0并行计算技术基础(9)

 

前几讲的链接:

 

 .NET4.0并行计算技术基础(7)

 .NET4.0并行计算技术基础(8)

=====================================

 

19.3.8 任务的取消

         TPL 提供了多个方式取消一个任务。

1 调用 Task.Cancel 方法直接取消任务的执行

         如果要取消一个正在运行的任务,可以调用 Task.Cancel 方法,此方法会设置 Task 对象的 IsCancellationRequested 属性等于 true 。在任务函数中,通过检查此属性值就可以知道是否需要取消操作。其代码框架如下:

 

    // 需要执行的任务函数

    Action taskFunction = delegate()

    {

        //... 代码略

        if (Task.Current.IsCancellationRequested )

          {

                // 处理外界的取消请求……

                // 设置 Task 的状态为 IsCanceled

                Task.Current.AcknowledgeCancellation();

                return;

          }

        //... 代码略

    };

 

         注意在任务函数中如何引用到当前的 Task 对象。

         以下代码先启动任务,然后再取消它:

 

    Task tsk = new Task(taskFunction);

    tsk.Start();

      //…

    tsk.Cancel(); // 取消操作

 

         示例项目 TaskCancel 展示了如何使用 Task.Cancel() 方法取消一个正在执行的任务。

         Task.Cancel() 是一个异步方法,它不会等待 Task 对象完成取消操作,仅仅是设置其 IsCancellationRequested 属性等于 true

        

         注意:

区分IsCancellationRequestedIsCanceled 属性

       Task 类的IsCancellationRequestedIsCanceled 属性“长得很像”,但却是不一样的,前者只是表明Task 类的Cancel 方法被调用了,而IsCanceled 属性表明Task 对象处于 Canceled 状态,这是Task 对象的3 个终止状态之一。

       这两个属性有着密切的联系。

(1)      Task.Cancel() 方法负责设置IsCancellationRequested 属性,而Task. AcknowledgeCancellation() 方法负责设置IsCanceled 属性。

(2)      Task.Cancel() 方法通常是由“外界”调用的,它表明外界希望“你”取消当前的工作。而Task. AcknowledgeCancellation() 方法是“你自己”在“内部”调用的,向“外界”表明:我已经停止执行当前的工作任务,我的当前状态为“Canceled ”。

(3)      调用Task.AcknowledgeCancellation() 方法时,要求自身的IsCancellationRequested 属性值为true ,否则会抛出一个InvalidOperationException 异常。

 

         如果发出取消请求的线程希望等待 Task 对象完成取消工作,可以改为调用 Task.CancelAndWait() 方法发出取消请求,这是一个同步方法,只有它返回之后,发出取消请求的线程才能继续执行。

         为了避免发生由于 Task.CancelAndWait() 长久不返回而导致发出取消请求的线程无限期阻塞的情况,可以指定一个等待的最长时间,或者是指定一个 CancellationToken ,从而允许“取消”这个“等待 Task 对象完成取消工作”的操作。 Task.CancelAndWait() 方法有几个重载形式用于这一场景,其中一个“功能最强”的形式如下:

 

public bool CancelAndWait(

    int millisecondsTimeout,

    CancellationToken cancellationToken

)

 

         millisecondsTimeout 指定等待时间, cancellationToken 用于指定一个取消令牌对象。

         当“时间到”或者 cancellationToken 对象的 IsCancellationRequested 属性值等于 True 时, CancelAndWait() 方法将立刻返回,其返回值为 false

2 使用线程统一取消模型在“外界”直接取消任务

         我们在第 17 章中介绍过 .NET 4.0 所提供的线程统一取消模型。可以在任务函数中直接监控一个 CancellationToken 对象而实现任务的取消工作。使用这个方法,无需直接调用 Task.Cancel() 就可以取消操作。如果读者掌握了线程统一取消模型,那么,要利用它来取消一个由 Task 启动的工作任务是非常简单的事,这个不妨留为读者的一个练习。

3 取消由 Parallel 类启动的并行计算

         如果并行计算是通过 Parallel.Invoke Parallel.For Parallel.ForEach 方法启动的,因为这里并没有直接地提供 Task 对象可供调用其 Cancel 方法,所以 TPL 采用其他方式来取消这种并行循环。

         Parallel.For Parallel.Invoke 可以接收一个 ParallelOptions 类型的参数 , 它包容一个 CancellationToken 对象,因而可以直接使用线程统一取消模型来取消。以下是框架代码:

 

CancellationTokenSource cts = new CancellationTokenSource();

ParallelOptions options = new ParallelOptions{CancellationToken = cts.Token};

Parallel.For( 循环起始值 , 循环终止值 , options,i=> 需要并行执行的函数 ());

// ……

// “外界”可通过调用 cts.Cancel() 请求终止并行计算任务

 

         示例项目 ParallelInvokeCancel 展示了如何使用线程统一取消模型来取消并行计算任务:

 

 

示例是一个 Windows Form 项目,使用 Parallel.Invoke 启动了 3 个并行工作任务,在任务函数中,监控 CancellationToken ,当发现有“取消”请求时,工作任务抛出一个 OperationCanceledException 异常给外界。

         请注意一下示例程序是如何捕获并处理异常的,下一小节将介绍并行计算中的异常处理机制。

4 并行循环的取消

         Parallel.For Parallel.ForEach 启动的是一个并行循环,它们都提供了多个重载的形式,以下列出了 Parallel.For 的一个函数重载形式:

 

public static ParallelLoopResult For(

    int fromInclusive, int toExclusive,

    ParallelOptions parallelOptions ,

    Action<int, ParallelLoopState > body);

 

         在上述函数声明中可以看到, Parallel.For 可以接收一个 ParallelOptions 类型的参数,因此,我们可以在并行循环中通过监控 CancellationToken 来检查是否外界提出了“取消”请求,但对“取消”请求的处理方式与前面介绍的 Parallel.Invoke 略有不同。

         Parallel.Invoke 中途取消任务标准的做法是抛出一个 OperationCanceledException 异常给外界,通知外界任务没有运行结束而中途取消。

         Parallel.For 的函数声明中,我们看到并行循环体(即上述声明中 body 参数所引用的函数)接收一个 ParallelLoopState 类型的参数,这个参数可以用于中途取消或停止并行循环。

         在启动并行循环时,任务并行库会为每一个执行并行循环体的线程关联上一个独立的 ParallelLoopState 对象,通过调用此对象的 Stop() 方法来停止并行循环。       

         如果在一个并行循环中调用了 ParallelLoopState.Stop() 方法,那么,任务并行库将不会再创建新线程来执行并行循环。等到当前所有正在执行此并行循环的线程终止时,整个并行循环将“优雅”地退场,不会引发异常。

         当并行循环以这种方式“提前结束”时, Parallel.For Parallel.ForEach 方法返回值(是一个 ParallelLoopResult 类型的变量)的 IsCompleted 属性等于 false ,如果并行循环正常完成, IsCompleted 属性等于 true

         一个问题出现了:

         一个线程通过调用ParallelLoopState.Stop() 方法中止了它所执行的并行循环,Parallel.For Parallel.ForEach 方法创建的其他相关的线程如何知道发生了这件事?

         回答:

         任务并行库还没有“聪明”到这种能“自动感知”的程度,必须由软件工程师来做这件事。

         请注意 ParallelLoopState 类型有一个 IsStopped 属性可以用于“通知”其他的线程。只要这些线程在执行自己的工作

抱歉!评论已关闭.