LoginSignup
18

More than 5 years have passed since last update.

【備忘録】ThreadPoolを使った非同期処理

Last updated at Posted at 2015-03-01

はじめに

明日の自分のためにメモっとく。

ログの内容見ると意図したとおりに別スレッドで動作しているけど、スレッドの終了を終了通知イベントで処理したけど、これが正解なんでしょうかね?

async/await使うとどういった書き方になるんだろうか。。。

環境

  • Windows7 Professional SP1
  • Microsoft Visual Studio Version 12.0.31101.00 Update 4
  • Microsoft .NET Framework Version 4.5.51209
  • Microsoft Visual C# 2013
  • NLog 3.1.0.0

Example

Program.cs
    /// <summary>
    /// 【備忘録】ThreadPoolを使った非同期処理
    /// </summary>
    class ThreadPool01
    {
        private static NLog.Logger logger = LogManager.GetLogger("fooLogger");
        // 発行したスレッドの数
        private static int maxThreadCount = 0;
        // 終了したスレッドの数
        private static int endThreadCount = 0;
        // 発行した全スレッドの終了状態
        private static bool isThreadFinished = false;
        // 作成するスレッドの数
        private static int makeThreads = 50;
        // スレッド プールがオンデマンドで作成するワーカー スレッドの最小数
        private static int workerThreads = makeThreads;
        // スレッド プールがオンデマンドで作成する非同期 I/O スレッドの最小数
        private static int completionPortThreads = makeThreads;

        /// <summary>
        /// メインメソッド
        /// </summary>
        /// <param name="args">実行引数</param>
        static void Main(string[] args)
        {
            try
            {
                logger.Info("Start. ");

                // スレッド プールの最小数を設定
                ThreadPool.SetMinThreads(workerThreads, completionPortThreads);

                WorkerClass workerClass = new WorkerClass(logger);
                // スレッド終了通知イベント
                workerClass.ThreadFinishedEventHandler += ThreadFinished;
                // スレッドプールスレッドが実行するメソッドを割り当て
                WaitCallback waitCallback = new WaitCallback(workerClass.WorkerProcess);

                for (int i = 0; i < makeThreads; i++)
                {
                    // キューにメソッド登録
                    ThreadPool.QueueUserWorkItem(waitCallback, i);
                    maxThreadCount++;
                }

                // 発行したスレッドが全て終了するまで待機
                while (!isThreadFinished)
                {
                    logger.Info("待機中...。");
                    Thread.Sleep(100);
                }
            }
            finally
            {
                logger.Info("End.");

                Console.WriteLine("Press any key.");
                Console.ReadKey();
            }
        }

        /// <summary>
        /// スレッド終了イベント処理
        /// 発行したスレッドが全て終了したら isThreadFinished = true になる
        /// </summary>
        /// <param name="sender">イベントハンドラ呼び出し元</param>
        /// <param name="e">イベントデータ</param>
        static void ThreadFinished(object sender, EventArgs e)
        {
            endThreadCount++;
            isThreadFinished = (maxThreadCount <= endThreadCount);
        }
    }
WorkerClass.cs
    /// <summary>
    /// 別スレッド用のクラス
    /// </summary>
    class WorkerClass
    {
        private Logger logger;

        /// <summary>
        /// 終了通知イベントハンドラ
        /// </summary>
        public event EventHandler ThreadFinishedEventHandler;

        /// <summary>
        /// 終了通知イベント
        /// </summary>
        /// <param name="e">イベントデータ</param>
        protected virtual void OnThreadFinished(EventArgs e)
        {
            if (ThreadFinishedEventHandler != null)
            {
                ThreadFinishedEventHandler(this, e);
            }
        }

        /// <summary>
        /// カスタムコンストラクタ
        /// </summary>
        /// <param name="logger">NLog</param>
        public WorkerClass(NLog.Logger logger)
        {
            this.logger = logger;
        }

        /// <summary>
        /// 別スレッドで実行するメソッド
        /// </summary>
        /// <param name="state">メソッドが使用するデータを格納したオブジェクト</param>
        public void WorkerProcess(object state)
        {
            try
            {
                for (int i = 0; i < 3; i++)
                {
                    logger.Info("[{0}] ループカウンタ:{1}", state.ToString(), i);
                    Thread.Sleep(100);
                }
            }
            finally
            {
                // 処理が終わったので終了イベントを通知
                OnThreadFinished(EventArgs.Empty);
            }
        }
    }
20150301.log
2015-03-01 17:03:52.4781 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() Start. 
2015-03-01 17:03:52.5021 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() 待機中...。 
2015-03-01 17:03:52.5141 [       3] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [0] ループカウンタ:0 
2015-03-01 17:03:52.5141 [       6] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [2] ループカウンタ:0 
2015-03-01 17:03:52.5141 [       4] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [1] ループカウンタ:0 
2015-03-01 17:03:52.6231 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() 待機中...。 
2015-03-01 17:03:52.6231 [       4] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [1] ループカウンタ:1 
2015-03-01 17:03:52.6231 [       6] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [2] ループカウンタ:1 
2015-03-01 17:03:52.6231 [       3] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [0] ループカウンタ:1 
2015-03-01 17:03:52.7371 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() 待機中...。 
2015-03-01 17:03:52.7451 [       4] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [1] ループカウンタ:2 
2015-03-01 17:03:52.7451 [       6] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [2] ループカウンタ:2 
2015-03-01 17:03:52.7632 [       3] [INFO ] Shusaku.Examples.WorkerClass.WorkerProcess() [0] ループカウンタ:2 
2015-03-01 17:03:52.8452 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() 待機中...。 
2015-03-01 17:03:52.9502 [       1] [INFO ] Shusaku.Examples.ThreadPool01.Main() End. 

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18