How to get a second System.Thread.ThreadPool?

前端 未结 6 1953
自闭症患者
自闭症患者 2021-01-18 23:16

If I use the ThreadPool in a nested way, my application hangs:

 ThreadPool.QueueUserWorkItem((state) =>
      ThreadPool.QueueUserWorkItem(Action));
         


        
6条回答
  •  日久生厌
    2021-01-19 00:00

    One way is using BlockingCollection. This is the class i build for it:

    Updated at 2018-04-23:

    public class WorkerPool : IDisposable
    {
        BlockingCollection queue = new BlockingCollection();
        List taskList;
        private CancellationTokenSource cancellationToken;
        int maxWorkers;
        private bool wasShutDown;
    
        int waitingUnits;
    
        public WorkerPool(CancellationTokenSource cancellationToken, int maxWorkers)
        {
            this.cancellationToken = cancellationToken;
            this.maxWorkers = maxWorkers;
            this.taskList = new List();
        }
        public void enqueue(T value)
        {
            queue.Add(value);
            waitingUnits++;
        }
        //call to signal that there are no more item
        public void CompleteAdding()
        {
            queue.CompleteAdding();          
        }
    
        //create workers and put then running
        public void startWorkers(Action worker)
        {
            for (int i = 0; i < maxWorkers; i++)
            {
                taskList.Add(new Task(() =>
                {
                    string myname = "worker " + Guid.NewGuid().ToString();
    
                    try
                    {
                        while (!cancellationToken.IsCancellationRequested)
                        {                     
                            var value = queue.Take();
                            waitingUnits--;
                            worker(value);
                        }
                    }
                    catch (Exception ex) when (ex is InvalidOperationException)  //throw when collection is closed with  CompleteAdding method. No pretty way to do this.
                    {
                        //do nothing
                    }
                }));
            }
    
            foreach (var task in taskList)
            {
                task.Start();
            }
        }
    
        //wait for all workers to be finish their jobs
        public void await()
        {
            while (waitingUnits >0 || !queue.IsAddingCompleted)
                Thread.Sleep(100);
    
            shutdown();
        }
    
        private void shutdown()
        {
            wasShutDown = true;
            Task.WaitAll(taskList.ToArray());            
        }
    
        //case something bad happen dismiss all pending work
        public void Dispose()
        {
            if (!wasShutDown)
            {
                queue.CompleteAdding();
                shutdown();
            }
        }
    }
    

    To see it working build this unit test:

    [TestMethod]
    public void workerPoolTest()
    {
        WorkerPool workerPool = new WorkerPool(new CancellationTokenSource(), 5);
    
        workerPool.startWorkers(value =>
        {
            log.Debug(value);
        });
    
        for (int i = 0; i < 100; i++)
        {
            workerPool.enqueue(i);
        }
        workerPool.CompleteAdding();
        workerPool.await();
    }
    

    Now you can have as many polls has you like, just by creating new WorkPool objects.

    Note that the code is not fully tested.

提交回复
热议问题