/// <summary>
 /// Get an abortable thread from the pool. Wait until Run() is called on the returned IThreadControl.
 /// </summary>
 /// <param name="work">actual work to be executed by this thread</param>
 /// <param name="arg">application arg</param>
 /// <param name="done">called when the work is finished</param>
 /// <returns>control object for the abortable thread</returns>
 public IThreadControl GetThread(WaitCallback work, object arg, CompletionCallback done)
 {
     ThreadControl ctl;
     lock (_Sync)
     {
         if (_Free.Count > 0)
         {
             ctl = _Free.Pop();
             ctl.Proc = work;
             ctl.Arg = arg;
         }
         else if (_Busy.Count < _Capacity)
         {
             ctl = new ThreadControl(_Prefix, this) { Proc = work, Arg = arg };
             ctl.Start();
         }
         else return null;
         _Busy.Add(ctl);
     }
     ctl.OnCompletion = done;
     return ctl;
 }
 public void Start(int minThreads, int maxThreads)
 {
     lock (_Sync)
     {
         if (_Recycler != null && _Recycler.IsAlive) throw new InvalidOperationException("Thread pool already initialized");
         _Capacity = maxThreads;
         _Free = new Stack<ThreadControl>(maxThreads);
         _Busy = new HashSet<ThreadControl>();
         _Run = true;
         _Recycler = new Thread(RecyclerAborter) { Name = _Prefix + "-recycler", IsBackground = true };
         _Recycler.Start();
         for (int i = 0; i < minThreads; i++)
         {
             var ctl = new ThreadControl(_Prefix, this);
             _Free.Push(ctl);
             ctl.Start();
         }
     }
 }