いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

スレッドの処理のいろいろな実装の仕方のメモ (Thread, ThreadPool, Delegate, BackgroundWorker)


以下、メモ書きです。


スレッド処理を書き方をいろいろなパターンで書いてみました。


実際に別スレッドにて処理を行なう際には以下のようなやり方があります。

  • Threadクラスを直接利用する。
  • ThreadPoolクラスにタスクをキューして実行してもらう。
  • 非同期デリゲートを利用する。(BeginInvoke, EndInvoke)
  • BackgroundWorkerを利用する。


以下、サンプルです。

// vim:set ts=4 sw=4 et ws is nowrap ft=cs:
using System;
using System.ComponentModel;
using System.Data;
using System.Data.Common;
using System.Reflection;
using System.Runtime.Remoting;
using System.Threading;
using System.Windows.Forms;

namespace Gsf.Samples{

    public class SampleLauncher{

        static void Main(string[] args){

            string className = typeof(Dummy).Name;
            if(args.Length != 0){
                className = args[0];
            }

            if(!string.IsNullOrEmpty(className)){
                className = string.Format("{0}.{1}", typeof(SampleLauncher).Namespace, className);
            }

            Assembly     assembly = Assembly.GetExecutingAssembly();
            ObjectHandle handle   = Activator.CreateInstance(assembly.FullName, className);
            if(handle != null){
                object clazz = handle.Unwrap();

                if(clazz != null){
                    (clazz as IExecutable).Execute();
                }
            }
        }
    }

#region "共通インターフェース定義"
    interface IExecutable{
        void Execute();
    }
#endregion

#region "ダミークラス"
    class Dummy : IExecutable{

        public void Execute(){
            Console.WriteLine("THIS IS DUMMY CLASS.");
        }
    }
#endregion

#region "スレッドを直接作成"
    class ThreadSample : IExecutable{

        object _lockObject = new object();
        int    _count      = 0;

        class ThreadParameter{
            public int Count{
                get;
                set;
            }

            public DateTime Time{
                get;
                set;
            }
        }

        public void Execute(){
            //
            // ThreadStartデリゲートを用いた場合.
            //
            ThreadStart ts = () => {
                lock(_lockObject){
                    if(_count < 10){
                        _count++;
                    }
                }

                Console.WriteLine("Count={0}", _count);
            };

            for(int i = 0; i < 15; i++){
                Thread t = new Thread(ts);
                t.IsBackground = false;

                t.Start();

                //
                // 確実にスレッドの走る順序を揃えるには以下のようにする。
                // (もっともこれをやるとスレッドの意味がないが・・)
                //
                //t.Join();
            }

            //
            // ParameterizedThreadStartを用いた場合.
            //
            ParameterizedThreadStart pts = (data) => {
                ThreadParameter p = data as ThreadParameter;
                Thread.Sleep(150);
                Console.WriteLine("Thread Count:{0}, Time:{1}", p.Count, p.Time.ToString("hh:mm:ss.fff"));
            };

            for(int i = 0; i < 15; i++){
                Thread t = new Thread(pts);
                t.IsBackground = false;

                t.Start(new ThreadParameter{Count=i, Time=DateTime.Now});

                //
                // 確実にスレッドの走る順序を揃えるには以下のようにする。
                // (もっともこれをやるとスレッドの意味がないが・・)
                //
                //t.Join();
            }
        }
    }
#endregion

#region "スレッドプールを利用したスレッド処理"
    class ThreadPoolSample : IExecutable{

        class StateInfo{
            public int Count{
                get;
                set;
            }

            public DateTime Time{
                get;
                set;
            }
        }

        public void Execute(){

            for(int i = 0; i < 15; i++){
                ThreadPool.QueueUserWorkItem*1;
                }, new StateInfo{Count=i, Time=DateTime.Now});
            }

            Thread.Sleep(2000);
        }
    }
#endregion

#region "非同期デリゲートを利用したスレッド処理"
    class AsyncDelegateSample : IExecutable{

        public void Execute(){

            Console.WriteLine("[MAIN] START. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);

            MethodInvoker invoker = () => {
                Console.WriteLine("[DELE] START. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);

                for(int i = 0; i < 10; i++){
                    Console.WriteLine("[DELE] PROCESSING. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(105);
                }
            };

            IAsyncResult asyncResult = invoker.BeginInvoke(
                (ar) => {
                    MethodInvoker caller = ar.AsyncState as MethodInvoker;
                    caller.EndInvoke(ar);
                    Console.WriteLine("[DELE] END. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                },
                invoker
            );

            for(int i = 0; i < 10; i++){
                Console.WriteLine("[MAIN] PROCESSING. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(100);
            }

            Thread.Sleep(3500);
            Console.WriteLine("[MAIN] END. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
        }
    }
#endregion

#region "BackgroundWorkerを利用したスレッド処理"
    class BackgroundWorkerSample : IExecutable{

        public void Execute(){
            Console.WriteLine("[MAIN] START. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);

            BackgroundWorker worker = new BackgroundWorker();

            worker.DoWork += (s, e) => {
                Console.WriteLine("[WORK] START. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);

                for(int i = 0; i < 10; i++){
                    Console.WriteLine("[WORK] PROCESSING. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                    Thread.Sleep(105);
                }
            };

            worker.RunWorkerCompleted += (s, e) => {
                if(e.Error != null){
                    Console.WriteLine("[WORK] ERROR OCCURED. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                }

                Console.WriteLine("[WORK] END. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
            };

            worker.RunWorkerAsync();

            for(int i = 0; i < 10; i++){
                Console.WriteLine("[MAIN] PROCESSING. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(100);
            }

            Thread.Sleep(1000);
            Console.WriteLine("[MAIN] END. ThreadId:{0}", Thread.CurrentThread.ManagedThreadId);
        }
    }
#endregion
}

*1:stateInfo) => { StateInfo p = stateInfo as StateInfo; Thread.Sleep(150); Console.WriteLine("Thread Count:{0}, Time:{1}", p.Count, p.Time.ToString("hh:mm:ss.fff"