2014年06月15日

C#でワーカスレッドを安全に終了させるまで待機

   このエントリーをはてなブックマークに追加 Clip to Evernote
どう書く?orgに感謝を込めて」シリーズ その16

■問題 (出題者:todogzm さん)
スレッドプールに複数のワーカスレッドが待機しており、メインスレッドはいつでもワーカスレッドに仕事を渡せるような状態になっているとします。
さて、メインスレッドからスレッドプールにいくつか仕事を与え、メインスレッドは与えた仕事すべてが終了するまで待機し、次の処理に行ってはいけない、というようなコードを書いてください。
#現実に書く機会が多そうなコードですね…。
ここでの仕事の内容は、適当に5秒から15秒の間スレッドをスリープする、というもので結構です。 また、ワーカスレッドのスレッドプール自体の使用を終了するか、または残して再利用するかは問いません。できればコメントにスレッドプールを残したかどうかを書いてください。

汎用的なクラスMyThreadPoolクラスを作成しています。
戻り値を得たいときは、MyThreadPoolクラスをちょっと改造すればOK!
使い方は、Mainメソッド見てもらえれば分かると思います。

MyThreadPoolのようなクラスを作れば、仕事が終わったかどうかは、MyThreadPoolクラス側がやってくれるので、仕事をする MyWork メソッドは、仕事そのものに専念できます。
.NET 4.5 の今ならばTaskクラス使ったほうがいいかなと思いますが...

■C#で書いたコード
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading;

namespace Doukaku.Org {
    class Program {

        static void Main(string[] args) {
            MyThreadPool tp = new MyThreadPool();
            tp.Enqueue(MyWork,7);
            tp.Enqueue(MyWork,12);
            tp.Enqueue(MyWork,15);
            tp.Enqueue(MyWork,2);
            tp.WaitAll();
            Console.WriteLine("Completed");
            Console.ReadLine();
        }

        static void MyWork(object state) {
            var id = System.Threading.Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine("Stread Start {0} arg={1}",id, state);
            Thread.Sleep((int)state * 1000);
            Console.WriteLine("Stread End {0}", id);
        }
    }

    public class MyThreadPool {
        List<WorkItemInfo> list = new List<WorkItemInfo>();

        public void Enqueue(WaitCallback callback, params object[] args) {
            var workItemInfo = new WorkItemInfo {
                Args = args,
                MethodInfo = callback.Method,
                AutoResetEvent = new AutoResetEvent(false),
            };
            list.Add(workItemInfo);
            WaitCallback wc = new WaitCallback(this.Call);
            ThreadPool.QueueUserWorkItem(wc, workItemInfo);           
        }

        private void Call(object state) {
            WorkItemInfo info = (WorkItemInfo)state;
            info.MethodInfo.Invoke(null, info.Args);
            info.AutoResetEvent.Set();
        }

        public void WaitAll() {
            foreach (var info in list) {
               info.AutoResetEvent.WaitOne();
            }
        }

        private class WorkItemInfo {
            public MethodInfo MethodInfo { get; set; }
            public object[] Args { get; set; }
            public AutoResetEvent AutoResetEvent { get; set; }
        }
    }
}


 

この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/gushwell/52372988