天天看點

C#線程安全使用(四)

這是時隔多年第四篇,主要是因為身在東軟受内網限制,好多文章就隻好發到東軟内部網站,懶的發到外面,現在一點點把在東軟寫的文章給轉移出來。

這裡主要講解下CancellationTokenSource,CancellationTokenSource是用于取消線程,具體使用起來有點另類:首先定義實體,然後将其下的屬性ToKen傳遞給線程,當需要取消線程時,調用下Cancel()方法。例子我依然采用了MSDN的例子,但我做了一些修改,這個例子雖然看起來挺複雜,但還是記錄了許多内容。

由于不好了解,我就粗略講解下:

Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(), 上面是建立任務,建立10個線程,并且線程中增加了判斷,如果随即數等于0就取消該線程。

 再介紹下factory.ContinueWhenAll,他包含兩個參數Task[] tasks,

Action<Task[]> continuationAction。MSDN的解釋是:

ContinueWhenAll

 方法執行 continuationAction 委托,在 tasks 數組的所有任務完成後,無論它們的完成狀态。

MSDN上就這個翻譯的還不錯,其他的基本可以無視了。。。

繼續看代碼,代碼中增加了try catch,這是為什麼呢,看下

英文解釋:

The exception that is thrown when the tasks array is empty

這裡千萬不能看中文解釋,不然你會淩亂的。看了英文解釋就懂了,讓任務為空就抛異常。

那麼為什麼任務為空呢,因為任務已經被取消了啊,是以為空了。具體代碼如下。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
    public static void Main()
    {
        // Define the cancellation token.
        CancellationTokenSource source = new CancellationTokenSource();
        CancellationToken token = source.Token;
        Random rnd = new Random();
        Object lockObj = new Object();
        List<Task<int[]>> tasks = new List<Task<int[]>>();
        TaskFactory factory = new TaskFactory(token);
        for (int taskCtr = 0; taskCtr <= 10; taskCtr++)
        {
            int iteration = taskCtr + 1;
            tasks.Add(factory.StartNew(() =>
            {
                int value;
                int[] values = new int[10];
                for (int ctr = 1; ctr <= 10; ctr++)
                {
                    lock (lockObj)
                    {
                        value = rnd.Next(0, 101);
                    }
                    if (value == 0)
                    {
                        source.Cancel();
                        Console.WriteLine("Cancelling at task {0}", iteration);
                        break;
                    }
                   
                    values[ctr - 1] = value;
                }
                Console.WriteLine("NO Cancel at task {0}", iteration);
                return values;
            }, token));
        }
        try
        {
            Task<double> fTask = factory.ContinueWhenAll(tasks.ToArray(),
                                                         (results) =>
                                                         {
                                                             Console.WriteLine("Calculating overall mean...");
                                                             long sum = 0;
                                                             int n = 0;
                                                             foreach (var t in results)
                                                             {
                                                                 foreach (var r in t.Result)
                                                                 {
                                                                     sum += r;
                                                                     n++;
                                                                 }
                                                             }
                                                             return sum / (double)n;
                                                         }, token);
            Console.WriteLine("The mean is {0}.", fTask.Result);
        }
        catch (AggregateException ae)
        {
            foreach (Exception e in ae.InnerExceptions)
            {
                if (e is TaskCanceledException)
                    Console.WriteLine("Unable to compute mean: {0}",
                                      ((TaskCanceledException)e).Message);
                else
                    Console.WriteLine("Exception: " + e.GetType().Name);
            }
        }
        Console.ReadLine();
        
    }
}      

顯示結果圖檔,每次的結果都不一樣的,是以我也是運作了好幾次,看這個結果會發現一件事,線程隻執行了兩個,即當線程2中調用Cancel後,其他線程也被取消了。

C#線程安全使用(四)