Hatena::ブログ(Diary)

だれかの雑記

2010-10-15 C# で メッセージキュー

  • 動機

C#スレッド処理を行うときメッセージキューが使いたくなった

「受け取ったパラメータに対する処理」が別スレッドで動いて

渡すパラメータがキューイングされてれば良いって状況で

プロセス間メッセージとかそういうのは無くていいから

簡単に実現できねーかな〜と

別用途に書いたワーカースレッドクラスを転用したら

それらしい事ができたのでメモ


  • WorkerThread

別用途で書いたワーカースレッドクラス

単一のスレッド内でキューイングされたActionを処理する

殆どの場合はThreadPoolを直接使えば良いんだけど

発行した処理が同時実行された場合の同期が面倒だったので

こういうのを作った

    public class WorkerThread : IDisposable
    {
        private Queue<Action> jobs = new Queue<Action>();
        private volatile bool running = true;
        private ManualResetEvent ev = new ManualResetEvent(false);

        public WorkerThread()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(Execute));
        }

        private void Execute(object o)
        {
            while (ev.WaitOne())
            {
                if (!running) { break; }

                Action actor = null;
                lock (jobs)
                {
                    if (jobs.Count > 0)
                    {
                        actor = jobs.Dequeue();
                    }
                    else
                    {
                        ev.Reset();
                    }
                }
                if (actor != null)
                {
                    actor();
                }
            }
        }

        public void Add(Action job)
        {
            lock (jobs)
            {
                jobs.Enqueue(job);
                ev.Set();
            }
        }

        #region IDisposable メンバ

        public void Dispose()
        {
            running = false;
            ev.Set();
        }

        #endregion
    }

これ自体の使い方は new して Action をAddしていくだけ

Disposeもあるけど呼ばなくたっていい

 WorkerThread thread = new WorkerThread();
 thread.Add(ほげほげ);
  • 使い方

以下のクラスはメッセージキューを使ったワーカークラスのようなもので

Add でパラメータをキューに入れ

Run でキューからパラメータを取り出し処理する

といったイメージ

先程のクラスを転用すると以下のようにかける

    class Worker
    {
        MYCS.WorkerThread thread = new MYCS.WorkerThread();

        public void Add(string message)
        {
            thread.Add(delegate()
            {
                Run(message); 
            });
        }

        private void Run(string message)
        {
            Console.WriteLine(message);
        }
    }

簡単だね