For・ForEachのパラレル処理

        // サンプルデータ
        private List<string> lstData = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K" };

        /// <summary>
        /// 通常処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("For文");
            for (int intCount = 0; intCount < lstData.Count; intCount++)
            {
                Console.WriteLine(lstData[intCount]);
                System.Threading.Thread.Sleep(300);
            }

            Console.WriteLine("ForEach文");
            foreach (string strData in lstData)
            {
                Console.WriteLine(strData);
                System.Threading.Thread.Sleep(300);
            }
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:00:32.75
For文
A
B
C
D
E
F
G
H
I
J
K
ForEach文
A
B
C
D
E
F
G
H
I
J
K
Finish:12:00:39.36

 

これにParallelを適用すると、下記のようになる。

        // サンプルデータ
        private List<string> lstData = new List<string> { "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K" };

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("Parallel.For文");
            Parallel.For(0, lstData.Count, intCount =>
            {
                Console.WriteLine(lstData[intCount]);
                System.Threading.Thread.Sleep(300);
            });

            Console.WriteLine("Parallel.ForEach文");
            Parallel.ForEach(lstData, strData =>
            {
                Console.WriteLine(strData);
                System.Threading.Thread.Sleep(300);
            });
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:01:43.02
Parallel.For文
A
C
D
E
F
G
H
I
J
B
K
Parallel.ForEach文
A
E
D
F
B
G
H
C
I
J
K
Finish:12:01:44.24

並列に処理を行うため、全体の処理速度が向上している反面、処理の順番がバラバラになっていることに注意されたい。

 

並列処理内部でBreakを行いたい場合は下記のようになる。

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelStart_Click(object sender, EventArgs e)
        {
            Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            Console.WriteLine("Parallel.For文");
            Parallel.For(0, lstData.Count, (intCount, objLoopState) =>
            {
                Console.WriteLine(lstData[intCount]);

                if (lstData[intCount] == "G")
                {
                    objLoopState.Break();
                }

                System.Threading.Thread.Sleep(300);
            });

            Console.WriteLine("Parallel.ForEach文");
            Parallel.ForEach(lstData, (strData, objLoopState) =>
            {
                Console.WriteLine(strData);

                if (strData == "G")
                {
                    objLoopState.Break();
                }

                System.Threading.Thread.Sleep(300);
            });
            Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
        }
Start:12:07:37.11
Parallel.For文
A
C
B
D
E
F
G
Parallel.ForEach文
A
F
D
E
G
B
C
Finish:12:07:37.73

Gが処理されたタイミングでBreakをかけているが、すでに並列処理が走ってしまっている分に関しては処理されてしまうので注意。

 

また、外部のスレッドから処理を中断する場合は下記のようになる。

        // トークン
        private CancellationTokenSource objToken = null;

        /// <summary>
        /// パラレル処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void btnParallelStart_Click(object sender, EventArgs e)
        {
            try
            {
                await Task.Run(() =>
                {
                    // ParallelOptionsの構築
                    objToken = new CancellationTokenSource();
                    ParallelOptions options = new ParallelOptions();
                    options.CancellationToken = objToken.Token;

                    Console.WriteLine("Start:" + DateTime.Now.ToString("HH:mm:ss.ff"));
                    Console.WriteLine("Parallel.For文");
                    Parallel.For(0, lstData.Count, options, intCount =>
                    {
                        Console.WriteLine(lstData[intCount]);
                        System.Threading.Thread.Sleep(300);
                    });

                    Console.WriteLine("Parallel.ForEach文");
                    Parallel.ForEach(lstData, options, strData =>
                    {
                        Console.WriteLine(strData);
                        System.Threading.Thread.Sleep(300);
                    });
                    Console.WriteLine("Finish:" + DateTime.Now.ToString("HH:mm:ss.ff"));
                });
            }
            catch (OperationCanceledException ex)
            {
                // キャンセルされた際に入る例外処理
                Console.WriteLine("Cancel:" + DateTime.Now.ToString("HH:mm:ss.ff"));
            }
        }

        /// <summary>
        /// パラレル処理のキャンセル
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnParallelBreak_Click(object sender, EventArgs e)
        {
            if (objToken != null)
            {
                objToken.Cancel();
            }
        }