C#パラレルプログラミングのコマンドタスクパラレル(.Net Framework 4.0)


この文書は、個人が「C#パラレルプログラミング高度チュートリアル」を学習するためのノートで、いくつかの文章のコード例をまとめ、デバッグしました.今後の開発過程で運用できる.
最も基本的な使用、パラレルタスクの作成
Taskの概念は.Net Framework 4に登場している.従来のマルチスレッドプログラムではthreadが用いられていたが,多くの場合業務処理をTaskに分けている.このように人間の考え方に近いのは、結局Threadが業務との関連を説明できないからだ.ここでC#はtaskを直接提供しており、開発者にも簡単です.
Taskの使用は非常に簡単で、作業関数を定義し、Taskを作成し、Taskが実行を開始し、Wait/WaitAllがTaskの終了を待つ.
Sample 1パラレルタスクの作成
using System;
using System.Collections.Generic;
using System.Text;

using System.Threading.Tasks;

namespace CSParallel_Program
{
    class ThreadWork1
    {
        public ThreadWork1()
        { }

        public void run()
        {
            System.Console.WriteLine("ThreadWork1 run { ");
            for (int i = 0; i < 100; i++)
            {
                System.Console.WriteLine("ThreadWork1 : " + i);
            }
            System.Console.WriteLine("ThreadWork1 run } ");
        }
    }

    class ThreadWork2
    {
        public ThreadWork2()
        { }

        public void run()
        {
            System.Console.WriteLine("ThreadWork2 run { ");
            for (int i = 0; i < 100; i++)
            {
                System.Console.WriteLine("ThreadWork2 : " + i*i);
            }
            System.Console.WriteLine("ThreadWork2 run } ");
        }
    }

    class Program
    {
        static void StartT1()
        {
            ThreadWork1 work1 = new ThreadWork1();
            work1.run();
        }

        static void StartT2()
        {
            ThreadWork2 work2 = new ThreadWork2();
            work2.run();
        }
        static void Main(string[] args)
        {
            Task t1 = new Task(() => StartT1()); Task t2 = new Task(() => StartT2()); Console.WriteLine("Sample 3-1 Main {"); Console.WriteLine("Main t1 t2 started {"); t1.Start(); t2.Start(); Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main wait t1 t2 end {"); Task.WaitAll(t1,t2); Console.WriteLine("Main wait t1 t2 end }"); Console.WriteLine("Sample 3-1 Main }"); Console.ReadKey(); } } } 

スレッドのキャンセル
Taskはスレッドキャンセルの操作を提供し、使用も非常に簡単です.本質的には異常によってスレッドの実行を中断し、Taskの状態をCancel状態にする(ただし、この点はテストプログラムではTaskの状態はCancelではなくFaildであり、PCを交換してこの問題を解決できるとは限らないかもしれない).
スレッドのキャンセルを実現するには、手順が必要です.1)まずスレッドにキャンセルチェックポイント(T h o r w IfCancellationRequested)を追加します.ここでは、ワーク関数の先頭とwhileループの実行中です.
2)Main関数でCancellationTokenSourceオブジェクトを定義し、それをパラメータとして作業Taskに渡す.
3)実行時にCancellationTokenSource.Cancell()を呼び出し、スレッドのキャンセルをトリガーします.
4)Main関数でキャンセル異常(OperationCanceledException)をキャプチャし、スレッドが終了し、キャンセルに成功した.
Sample 2スレッドのキャンセル
using System;
using System.Collections.Generic;
using System.Text;

using System.Threading.Tasks;

namespace CSParallel_Program
{
    class ThreadWork1
    {
        public ThreadWork1()
        { }

        public void run_with_cancel(System.Threading.CancellationToken ct)
        {
            //try
            //{
                System.Console.WriteLine("ThreadWork1 run { ");
                ct.ThrowIfCancellationRequested();
                for (int i = 0; i < 100; i++)
                {
                    System.Console.WriteLine("ThreadWork1 : " + i);
                    System.Threading.Thread.Sleep(200);
                    ct.ThrowIfCancellationRequested();
                }
                System.Console.WriteLine("ThreadWork1 run } ");
            //}
            //catch (OperationCanceledException ex)
            //{
            //    System.Console.WriteLine("ThreadWork1 Get Exception : " + ex.ToString());
            //}
        }
    }

    class ThreadWork2
    {
        public ThreadWork2()
        { }

        public void run_with_cancel(System.Threading.CancellationToken ct)
        {
            //try
            //{
                ct.ThrowIfCancellationRequested();
                System.Console.WriteLine("ThreadWork2 run { ");
                for (int i = 0; i < 100; i++)
                {
                    System.Console.WriteLine("ThreadWork2 : " + i * i);
                    System.Threading.Thread.Sleep(300);
                    ct.ThrowIfCancellationRequested();
                }
                System.Console.WriteLine("ThreadWork2 run } ");
            //}
            //catch (OperationCanceledException ex)
            //{
            //    System.Console.WriteLine("ThreadWork2 Get Exception : " + ex.ToString());
            //}
        }
    }

    class Program
    {
        static void StartT1(System.Threading.CancellationToken ct)
        {
            ThreadWork1 work1 = new ThreadWork1();
            work1.run_with_cancel(ct);
        }

        static void StartT2(System.Threading.CancellationToken ct)
        {
            ThreadWork2 work2 = new ThreadWork2();
            work2.run_with_cancel(ct);
        }
        static void Main(string[] args)
        {
            System.Threading.CancellationTokenSource cts =
                new System.Threading.CancellationTokenSource();
            System.Threading.CancellationToken ct = cts.Token;

            Task t1 = new Task(() => StartT1(ct)); Task t2 = new Task(() => StartT2(ct)); Console.WriteLine("Sample 3-1 Main {"); Console.WriteLine("Main t1 t2 started {"); t1.Start(); t2.Start(); Console.WriteLine("Main t1 t2 started }"); Console.WriteLine("Main sleep 2 seconds and CANCEL {"); System.Threading.Thread.Sleep(2000); cts.Cancel(); Console.WriteLine("Main sleep 2 seconds and CANCEL }"); try { Console.WriteLine("Main wait t1 t2 end {"); if (!Task.WaitAll(new Task[] { t1, t2 }, 5000)) { Console.WriteLine("Worker1 and Worker2 NOT complete within 5 seconds"); Console.WriteLine("Worker1 Status: " + t1.Status); Console.WriteLine("Worker2 Status: " + t2.Status); } else { Console.WriteLine("Worker1 and Worker2 complete within 5 seconds"); } Console.WriteLine("Main wait t1 t2 end }"); } catch (AggregateException agg_ex) { foreach (Exception ex in agg_ex.InnerExceptions) { Console.WriteLine("Agg Exceptions: " + ex.ToString()); Console.WriteLine(""); } } if (t1.IsCanceled) { Console.WriteLine("Worker 1 is CANCELED"); } if (t2.IsCanceled) { Console.WriteLine("Worker 2 is CANCELED"); } Console.WriteLine("Sample 3-1 Main }"); Console.ReadKey(); } } } 

スレッドの戻り値
Taskは、Threadとは異なる戻り値サービスを提供することができ、Threadでは、C#でもC++でも共有キューなどでテスト結果が返されます.
戻り値を使用する手順:1)最初のステップでは、当然、作業関数に戻り値を定義し、実装中に有効な値を返します.
2)Taskを定義する場合はvarを使用してTaskを使用できなくなり、Task.Factoryも使用しなければなりません.var t1 = Task.Factory.StartNew(() => StartT1()); .後で結果を取得する際にはt 1.Resultが必要であり,t 1をTaskと定義するとResult属性がない.
3)Taskを実行する.
4)取得結果は,主にt 1.Result属性を用いた.
5)TaskCreationOptions.LongRunningについては、オペレーティングシステムがタスクを実行する際のいくつかの方法を指定することができます.もちろん、これらの方法はシステムによって制御されており、必ずしもタスクを定義する必要を満たすとは限りません.https://msdn.microsoft.com/zh-tw/library/system.threading.tasks.taskcreationoptions(v=vs.110).aspx
Sample 3スレッド戻り値
using System;
using System.Collections.Generic;
using System.Text;

using System.Threading.Tasks;


namespace CSParallel_Program
{
    class ThreadWork1
    {
        public ThreadWork1()
        { }

        public List<string> run()
        {
            List<string> RetList = new List<string>();
            System.Console.WriteLine("ThreadWork1 run { ");
            System.Console.WriteLine("ThreadWork1 running ... ... ");
            for (int i = 0; i < 100; i++)
            {
                RetList.Add("ThreadWork1 : " + i);
                //System.Console.WriteLine("ThreadWork1 : " + i);
            }
            System.Console.WriteLine("ThreadWork1 run } ");

            return RetList;
        }
    }

    class ThreadWork2
    {
        public ThreadWork2()
        { }

        public List<string> run()
        {
            List<string> RetList = new List<string>();

            System.Console.WriteLine("ThreadWork2 run { ");
            System.Console.WriteLine("ThreadWork2 running ... ... ");
            for (int i = 0; i < 100; i++)
            {
                RetList.Add("ThreadWork2 : " + i);
                //System.Console.WriteLine("ThreadWork2 : " + i * i);
            }
            System.Console.WriteLine("ThreadWork2 run } ");
            return RetList;
        }
    }

    class Program
    {
        static List<string> StartT1()
        {
            ThreadWork1 work1 = new ThreadWork1();
            return work1.run();
        }

        static List<string> StartT2()
        {
            ThreadWork2 work2 = new ThreadWork2();
            return work2.run();
        }
        static void Main(string[] args)
        {
            // For return value we can't use this one : new Task(() => StartT2());
            // The problem is the compiler will not know there is a return value.
            // if we use t2.Result after that, there will be a compiler error.
            var t1 = Task.Factory.StartNew(() => StartT1());
            var t2 = Task.Factory.StartNew(() => StartT2());

            Console.WriteLine("Sample 3-3 Main {");


            // If we use Task.Factory.StartNew, it's no need to use t1.start()
            //Console.WriteLine("Main t1 t2 started {");
            //t1.Start();
            //t2.Start();
            //Console.WriteLine("Main t1 t2 started }");

            Console.WriteLine("Main wait t1 t2 end {");
            Task.WaitAll(t1, t2);
            Console.WriteLine("Main wait t1 t2 end }");

            var t3 = Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("============= T1 Result =============");
                    for (int i = 0; i < t1.Result.Count; i++)
                    {
                        Console.WriteLine(t1.Result[i]);
                    }
                    Console.WriteLine("============= ========= =============

"
); Console.WriteLine("============= T2 Result ============="); for (int i = 0; i < t2.Result.Count; i++) { Console.WriteLine(t2.Result[i]); } Console.WriteLine("============= ========= =============

"
); }, TaskCreationOptions.LongRunning ); Console.WriteLine("Sample 3-3 Main }"); Console.ReadKey(); } } }

タスクの実行順序の制御
Taskはまた、タスクの実行順序を簡単に制御する方法ContinueWith()を提供する.
ContinueWithとWait関数を組み合わせて使用すると、タスクの実行順序を制御できます.
Sample 4制御スレッドの実行順序
using System;
using System.Collections.Generic;
using System.Text;

using System.Threading.Tasks;


namespace CSParallel_Program
{
    class ThreadWork1
    {
        public ThreadWork1()
        { }

        public List<string> run()
        {
            List<string> RetList = new List<string>();
            System.Console.WriteLine("ThreadWork1 run { ");
            System.Console.WriteLine("ThreadWork1 running ... ... ");
            for (int i = 0; i < 100; i++)
            {
                RetList.Add("ThreadWork1 : " + i);
                //System.Console.WriteLine("ThreadWork1 : " + i);
            }
            System.Console.WriteLine("ThreadWork1 run } ");

            return RetList;
        }
    }

    class ThreadWork2
    {
        public ThreadWork2()
        { }

        public List<string> run()
        {
            List<string> RetList = new List<string>();

            System.Console.WriteLine("ThreadWork2 run { ");
            System.Console.WriteLine("ThreadWork2 running ... ... ");
            for (int i = 0; i < 100; i++)
            {
                RetList.Add("ThreadWork2 : " + i);
                //System.Console.WriteLine("ThreadWork2 : " + i * i);
            }
            System.Console.WriteLine("ThreadWork2 run } ");
            return RetList;
        }
    }

    class Program
    {
        static void StartT0()
        {
            System.Console.WriteLine("Hello I am T0 Task, sleep 3 seconds. when I am ready others GO!");
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("StartT0 sleeping ... ... " + i);
                System.Threading.Thread.Sleep(1000);
            }
        }

        static List<string> StartT1()
        {
            ThreadWork1 work1 = new ThreadWork1();
            return work1.run();
        }

        static List<string> StartT2()
        {
            ThreadWork2 work2 = new ThreadWork2();
            return work2.run();
        }
        static void Main(string[] args)
        {
            Console.WriteLine("Sample 3-4 Main {");
            // The sequence of the task is:
            // T0 (Wait 3s) --> |
            //                  | --> T1 (Cacluate) |
            //                  | --> T2 (Cacluate) |
            //                                      |  --> T3 (Print)

            var t0 = Task.Factory.StartNew(() => StartT0()); var t1 = t0.ContinueWith((t) => StartT1()); var t2 = t0.ContinueWith((t) => StartT2()); Console.WriteLine("Main wait t1 t2 end {"); Task.WaitAll(t1, t2); Console.WriteLine("Main wait t1 t2 end }"); var t3 = Task.Factory.StartNew(() => { Console.WriteLine("============= T1 Result ============="); for (int i = 0; i < t1.Result.Count; i++) { Console.WriteLine(t1.Result[i]); } Console.WriteLine("============= ========= =============

"
); Console.WriteLine("============= T2 Result ============="); for (int i = 0; i < t2.Result.Count; i++) { Console.WriteLine(t2.Result[i]); } Console.WriteLine("============= ========= =============

"
); }, TaskCreationOptions.LongRunning)
; Console.WriteLine("Sample 3-4 Main }"); Console.ReadKey(); } } }

コードサンプルエンジニアリングのダウンロード