C#でTaskQueue

時間がかかる仕事をどんどんキューに放り込んでいくと、自分が持てるキャパを使ってどんどん仕事をさばいていくよーなことがしたかったんです。
たとえば、キャパが3つあったら、仕事を「仕事、仕事、仕事」と積んであげると、同時に実行します。
「仕事、仕事、仕事、仕事」と4つ積むと、最初の3つが並列で走って、真っ先に手が開いたスレッドが最後の4つ目の仕事をします。
ようするに、タスクスケジューラーみたいなことがやりたかったんですよ。

ThreadPool はアプリ全体で使いまわすらしいので、役に立たたなそうなので自分で作りました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1 
{
    public class TaskQueue : IDisposable
    {
        //仕事キュー
        private Queue<ThreadStart> _taskQueue = new Queue<ThreadStart>();

        //仕事をするスレッド
        private Thread[] _threads;

        //仕事ができたら通知してくれるイベント
        private AutoResetEvent _notifyEvent = new AutoResetEvent(false);

        //この変数はロックだ。そしてコレを見てくれる君達もロックだ。
        private object _lockObject = new object();

        //スレッド群の生成
        public TaskQueue(int threadCount)
        {
            this._threads = new Thread[threadCount];
            for (int i = 0; i < threadCount; i++)
            {
                this._threads[i] = new Thread(this.threadRunning);
                this._threads[i].Start();
            }
        }
        //一応、リソース解放時に停止命令を出すようにするよ。
        public void Dispose() 
        {
            this.stop();
        }

        //スレッド群の停止
        public void stop()
        {
            if (this._threads == null)
            {
                return;
            }

            int i;
            //終了のお知らせ
            for (i = 0; i < this._threads.Length; i++)
            {
                this._threads[i].Interrupt();
            }
            //確実にとめる
            for (i = 0; i < this._threads.Length; i++)
            {
                this._threads[i].Join();
                this._threads[i] = null;
            }
            this._threads = null;
        }

        //タスクを追加する
        public void addTask(ThreadStart inTask)
        {
            //タスクを追加
            lock (this._lockObject)
            {
                this._taskQueue.Enqueue(inTask);
            }
            //飯ができたぞー
            this._notifyEvent.Set();
        }

        //キューに残っている仕事の確認
        public int Count
        {
            get
            {
                lock (this._lockObject)
                {
                    return this._taskQueue.Count;
                }
            }
        }

        //スレッドの中で動作
        private void threadRunning()
        {
            try
            {
                ThreadStart task;
                while (true)
                {
                    //仕事が来るまで寝る。 ニートになる。リーマン乙。
                    this._notifyEvent.WaitOne();

                    while (true)
                    {
                        //何か仕事はないかな?
                        lock (this._lockObject)
                        {
                            if (this._taskQueue.Count <= 0)
                            {
                                //仕事がないので寝る
                                break;
                            }
                            //内定
                            task = this._taskQueue.Dequeue();
                        }
                        //仕事をする リーマン状態
                        task();
                        //次の仕事を探しに行く。
                    }
                }
            }
            catch (ThreadInterruptedException)
            {
                //おしまい
            }
        }
    };


    //ここから動作確認用のサンプルソース
    class Program
    {
        static void Main(string[] args)
        {
            //タスクキューの作成 最大3つまで同時動作
            TaskQueue task = new TaskQueue(3);

            while (true)
            {
                System.Console.WriteLine("タスクキューサンプル");
                System.Console.WriteLine(" タスクキュー3つまでなら同時に仕事をさばきます。");
                System.Console.WriteLine(" スペースキーを押して仕事を積みます。");
                System.Console.WriteLine("q             終了");
                System.Console.WriteLine("スペース      仕事を積む");
                System.Console.WriteLine("その他        画面の更新");
                ConsoleKeyInfo key = Console.ReadKey(false);
                Console.Clear();
                if (key.KeyChar == 'Q' || key.KeyChar == 'q')
                {
                    System.Console.WriteLine("( ゚д゚)ぽかーん");
                    break;
                }
                if (key.KeyChar == ' ')
                {
                    int time = (Environment.TickCount % 5000) + 1000;
                    string name = (time % 3 == 0) ? "開発業務" : (time % 3 == 1) ? "資料作成" : "サポート業務";
                    System.Console.WriteLine("( ゚д゚)< この仕事やっといて、今日中に。");

                    //タスクの追加
                    task.addTask(
                        delegate()
                        {
                            //なにか仕事をする
                            Thread.Sleep(time);
                            //終わったのでつぶやく。
                            System.Console.WriteLine("( ´∀`)< おわた。{0}ミリ秒かかる仕事({1})が終わりました。", time, name);
                        });

                    System.Console.WriteLine("( ´∀`)< {0}ミリ秒かかる仕事({1})が積まれました", time, name);
                }
                //現在の仕事の数を表示.
                int workCount = task.Count;
                System.Console.WriteLine("");
                System.Console.WriteLine("現在 {0} の仕事に手がつけられていません。", workCount);
                if (workCount > 5)
                {
                    System.Console.WriteLine("( ´∀`)< ブラック乙");
                }
                else if (workCount > 3)
                {
                    System.Console.WriteLine("( ´∀`)< 帰れない。。。");
                }
                else if (workCount > 2)
                {
                    System.Console.WriteLine("( ´∀`)< これは、終電だな");
                }
                else if (workCount > 0)
                {
                    System.Console.WriteLine("( ´∀`)< タスクオーバー。残業だな");
                }
                else if (workCount <= 0)
                {
                    System.Console.WriteLine("( ´∀`)< キャパ内の仕事量です。");
                }
                System.Console.WriteLine("");
                System.Console.WriteLine("");
                System.Console.WriteLine("");
                System.Console.WriteLine("");
            }
            //タスクキューに終了命令を出す
            task.stop();
        }
    }
}

んで、twitter にできたよーって書いたら、 @takeshik さんにガスガス修正いれられてしまった。
http://gist.github.com/436441
これがモダンな C# の書き方っすかー。