Best way to limit the number of active Tasks running via the Parallel Task Library

前端 未结 6 1901
春和景丽
春和景丽 2020-12-02 16:54

Consider a queue holding a lot of jobs that need processing. Limitation of queue is can only get 1 job at a time and no way of knowing how many jobs there a

6条回答
  •  心在旅途
    2020-12-02 17:32

    Potential flow splits and continuations caused by await, later on in your code or in a 3rd party library, won't play nicely with long running tasks (or threads), so don't bother using long running tasks. In the async/await world, they're useless. More details here.

    You can call ThreadPool.SetMaxThreads but before you make this call, make sure you set the minimum number of threads with ThreadPool.SetMinThreads, using values below or equal to the max ones. And by the way, the MSDN documentation is wrong. You CAN go below the number of cores on your machine with those method calls, at least in .NET 4.5 and 4.6 where I used this technique to reduce the processing power of a memory limited 32 bit service.

    If however you don't wish to restrict the whole app but just the processing part of it, a custom task scheduler will do the job. A long time ago, MS released samples with several custom task schedulers, including a LimitedConcurrencyLevelTaskScheduler. Spawn the main processing task manually with Task.Factory.StartNew, providing the custom task scheduler, and every other task spawned by it will use it, including async/await and even Task.Yield, used for achieving asynchronousy early on in an async method.

    But for your particular case, both solutions won't stop exhausting your queue of jobs before completing them. That might not be desirable, depending on the implementation and purpose of that queue of yours. They are more like "fire a bunch of tasks and let the scheduler find the time to execute them" type of solutions. So perhaps something a bit more appropriate here could be a stricter method of control over the execution of the jobs via semaphores. The code would look like this:

    semaphore = new SemaphoreSlim(max_concurrent_jobs);
    
    while(...){
     job = Queue.PopJob();
     semaphore.Wait();
     ProcessJobAsync(job);
    }
    
    async Task ProcessJobAsync(Job job){
     await Task.Yield();
     ... Process the job here...
     semaphore.Release();
    }
    

    There's more than one way to skin a cat. Use what you believe is appropriate.

提交回复
热议问题