並列処理グループの直列化(C#)

-- Index --

・Top

・Softwares

▼親父の独り言集

・Oracleデータベースに対する独り言集

▼VB.NETやC#に対する独り言集

・ご利用の前に
・Excel遅延バインディング読み書き高速化(C#)
・Excelデータ比較(C#)
→並列処理グループの直列化(C#)
・Treeコマンドもどき(C#)
・コマンドラインを配列に分割する(C#)
・相対パス取得(C#)
・ListViewのフォーカス行取得(VB.NET)
・DataGridViewでパスワードマスク

・我が家の家電事情について

いきさつ

コンソールアプリに限定ですが、ある処理を並列化し、 それらがすべて終わってから次の並列処理を行いたい場合、 C#やVB.NETではどう記述したらよいのでしょうか?
Microsoftはいかに簡潔にソースをコーディングできるかをずっと模索してきたみたいで いろいろと手法はあるらしく、いろんなサイトを見ても逆に困惑してきた・・・。 ずっとThreadをNewしてきた親父にはチンプンカンプンです。みんな頭良すぎです。 簡潔に書くとこんな感じだと思います。

参考ソース

/// Copyright(c) 2015 pakkin. All Rights Reserved.
/// [改訂履歴]
/// 2015.11.15 作成
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
    class Program
    {
        private static DateTime jobStart;                   // ジョブ単位の開始時刻
        private static DateTime allStart;                   // コンソール自体の開始時刻
        private static TimeSpan sumTime = TimeSpan.Zero;    // 総合時間
        private static object sync = new object();          // 同期用
        // メイン処理
        public static void Main(string[] args)
        {
            Console.WriteLine("main start.");
            allStart = DateTime.Now;

            // JobGroup1
            List<Task> jobGroup1 = new List<Task>();
            jobStart = DateTime.Now;
            Console.WriteLine("  JobGroup1 start.");
            Console.WriteLine("    ジョブ名       所要時間 バッチ開始から");
            Console.WriteLine("    -------------- -------- --------------");
            jobGroup1.Add(asyncJobWorker("並列ジョブG1-1", 5000));
            jobGroup1.Add(asyncJobWorker("並列ジョブG1-2", 4000));
            Task.WaitAll(jobGroup1.ToArray());
            Console.WriteLine("  JobGroup1 end.");

            // JobGroup2
            List<Task> jobGroup2 = new List<Task>();
            jobStart = DateTime.Now;
            Console.WriteLine("  JobGroup2 start.");
            Console.WriteLine("    ジョブ名       所要時間 バッチ開始から");
            Console.WriteLine("    -------------- -------- --------------");
            jobGroup2.Add(asyncJobWorker("並列ジョブG2-1", 2000));
            jobGroup2.Add(asyncJobWorker("並列ジョブG2-2", 5000));
            Task.WaitAll(jobGroup2.ToArray());
            Console.WriteLine("  JobGroup2 end.");

            TimeSpan allTime = DateTime.Now - allStart;
            Console.WriteLine("main end.");
            Console.WriteLine("total time       : {0}sec.", allTime.ToString().Substring(0, 8));
            Console.WriteLine("job time summary : {0}sec.", sumTime.ToString().Substring(0, 8));
            Console.ReadKey(true);
        }
        // 非同期ジョブ
        private static async Task asyncJobWorker(string name, int wait)
        {
            await Task.Run(() => { JobWorker(name, wait); });
        }
        // ジョブ本体
        private static void JobWorker(string name, int wait)
        {
            System.Threading.Thread.Sleep(wait);
            ConsoleWrite(name);
        }
        // コンソール出力
        private static void ConsoleWrite(string name)
        {
            // 他のスレッドに割り込まれたくないところはlockを使用
            // 多用するとボトルネックになるので使用するタイミングが重要
            lock (sync)
            {
                sumTime += DateTime.Now - jobStart;
                Console.Write("    " + name);
                Console.Write(" " + (DateTime.Now - jobStart).ToString().Substring(0, 8));
                Console.Write(" " + (DateTime.Now - allStart).ToString().Substring(0, 8));
                Console.Write("\r\n");
            }
        }
    }
}

実行結果

処理の流れはこんな感じだと思われます。重要なのはasyncではなく、awaitとwaitの関係。

・await・・・・・非同期待機の意だと思われます。つまり、"終わるまで待てませんよ"ってこと
・Task.Wait・・・"待たないっていったけど、やっぱ待ちます。今、この場所で"


お前たちは恋人同士か??親父をおちょくるのもいい加減にしなさいって感じです。


本来は、こんなコンソールのためではなく、長時間操作に対するUI画面フリーズ回避のためのものなので、実際にはこんな感じで使うものだと思います。

/// Copyright(c) 2015 pakkin. All Rights Reserved.
/// [改訂履歴]
/// 2015.11.22 作成
using System;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void button1_Click(object sender, EventArgs e)
        {
            // button1をロック
            this.button1.Enabled = false;
            this.Cursor = Cursors.WaitCursor;
            // 重たい処理
            await Task.Run(() =>
            {
                System.Threading.Thread.Sleep(10000);
            });
            // button1を解放
            this.Cursor = Cursors.Default;
            this.button1.Enabled = true;
        }
    }
}

Copyright(c) 2014-2017 pakkin. All Rights Reserved.