C# 多線程六之Task(任務)二


前面介紹了Task的由來,以及簡單的使用,包括開啟任務,處理任務的超時、異常、取消、以及如果獲取任務的返回值,在回去返回值之后,立即喚起新的線程處理返回值、且如果前面的任務發生異常,喚起任務如果有效的處理異常等關於Task的知識。所以本文將介紹Task更多的用法和特性.

 

一、如果通過一個任務創建多個子任務.

1、Task支持一個任務,創建多個子任務,並且保持關聯.

        static void Main(string[] args)
        {
            var parentTask = new Task<int[]>(() =>
            {
                //開啟多個子任務
                var results = new int[2];

                //創建子任務,並將子任務的值賦給results變量,並通過TaskCreationOptions.AttachedToParent,將其關聯到父任務,如果不指定,該任務將獨立於父任務單獨執行
                //這里有個奇怪的問題,只能使用new Task的方式去創建關聯到父任務的子任務,因為Task.Run沒有提供這個方法,可以通過擴展方法解決這個問題
                new Task(() => results[0] = ChildThreadOne(), TaskCreationOptions.AttachedToParent).Start();
                new Task(() => results[1] = ChildThreadTwo(), TaskCreationOptions.AttachedToParent).Start();

                return results;
            });
            parentTask.Start();
            parentTask.ContinueWith(x =>
            {
                Console.WriteLine("當父任務執行完畢時,CLR會喚起一個新線程,將父任務的返回值(子任務的返回值)輸出,所以這里不會有任何的線程發生阻塞");
                foreach (var re in parentTask.Result)
                {
                    Console.WriteLine("子任務的返回值分別為:{0}", re);
                }
            });
            Console.WriteLine("主線程不會阻塞,它會繼續執行");
            Console.ReadKey();//必須加這行代碼,因為Task時線程池線程,屬於后台線程
        }

        /// <summary>
        /// 子任務一
        /// </summary>
        static int ChildThreadOne()
        {
            Thread.Sleep(2000);//模擬長時間計算操作
            Console.WriteLine("子任務一完成了計算任務,並返回值:{0}", 6);
            return 6;
        }

        /// <summary>
        /// 子任務一
        /// </summary>
        static int ChildThreadTwo()
        {
            Thread.Sleep(2000);//模擬長時間計算操作
            Console.WriteLine("子任務二完成了計算任務,並返回值:{0}", 6);
            return 6;
        }

 

 

二、關於Task的資源釋放問題.

如果你看過Task的源碼,你會發現下面這個有趣的問題:

ok,你會想它想釋放什么呢?

沒錯,當Task任務,指定了TaskContinuationOptions枚舉狀態,且指定的值如下:

那么,直接return,什么資源釋放操作都不做.

如果任務沒有完成,就調用Dispose方法,那么直接拋異常,如果完成了,它就釋放了ManualResetEventSlim信號量(后面的文章會介紹).所以如果你在task中使用了其它的一些非托管資源,那么最好在代碼里自己手動釋放,在使用完之后。或者自己實現了Task的派生類,把需要用的非托管資源加進去,然后在使用完派生類之后,調用Dispose方法.

 

三、關於Task的幾個常用屬性

1、Id屬性,每個Task對象都有一個Id屬性,全局唯一,且每次創建新的任務,這個值都會遞增1.

2、TaskStatus狀態

    //
    // 摘要:
    //     表示 System.Threading.Tasks.Task 的生命周期中的當前階段。
    public enum TaskStatus
    {
        //
        // 摘要:
        //     該任務已初始化,但尚未被計划。
        Created = 0,
        //
        // 摘要:
        //     該任務正在等待 .NET Framework 基礎結構在內部將其激活並進行計划。
        WaitingForActivation = 1,
        //
        // 摘要:
        //     該任務已被計划執行,但尚未開始執行。
        WaitingToRun = 2,
        //
        // 摘要:
        //     該任務正在運行,但尚未完成。
        Running = 3,
        //
        // 摘要:
        //     該任務已完成執行,正在隱式等待附加的子任務完成。
        WaitingForChildrenToComplete = 4,
        //
        // 摘要:
        //     已成功完成執行的任務。
        RanToCompletion = 5,
        //
        // 摘要:
        //     該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 對取消進行了確認,此時該標記處於已發送信號狀態;或者在該任務開始執行之前,已向該任務的
        //     CancellationToken 發出了信號。 有關詳細信息,請參閱任務取消。
        Canceled = 6,
        //
        // 摘要:
        //     由於未處理異常的原因而完成的任務。
        Faulted = 7
    }

構造完Task對象是,狀態為Created,當任務啟動時,狀態變為WaitingToRun,當Task實際在線程上運行時,狀態變為Running.如果當前任務為父任務,且它已經執行完畢,等待其它子任務執行完畢的時候,其狀態變為WaitingForChildrenToComplete.如果任務完成可能會出現以下幾種狀態:RanToCompletion(已成功完成執行的任務)、Canceled(取消狀態)、

Faulted(任務出錯).

這里需要注意一個特殊的狀態WaitingForActivation

當使用Task對象的ContinueWith的Task對象處理改狀態,意味者該Task任務的調度由任務基礎結構控制.也就是該任務的調度只有當前面的任務執行完之后,由CLR發起執行調用.

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM