views:

232

answers:

4

I have just started to look at the new "System.Threading.Tasks" goodness in .Net 4.0, and would like to know if there is any build in support for limiting the number of concurrent tasks that run at once, or if this should be manually handled.

E.G: If I need to call a calculation method 100 times, is there a way to set up 100 Tasks, but have only 5 execute simultaneously? The answer may just be to create 5 tasks, call Task.WaitAny, and create a new Task as each previous one finishes. I just want to make sure I am not missing a trick if there is a better way to do this.

Basically, is there a built in way to do this:

Dim taskArray() = {New Task(Function() DoComputation1()),
                   New Task(Function() DoComputation2()),
                   ...
                   New Task(Function() DoComputation100())}

Dim maxConcurrentThreads As Integer = 5
RunAllTasks(taskArray, maxConcurrentThreads)

Thanks for any help.

A: 

It does not look like it, although you could create a subclass of TaskScheduler that implements such behavior. This article describes how TaskScheduler was subclassed to run tasks on separate threads; it might be a good start for the implementation you need.

Justin Ethier
A: 

Short answer: If what you want is to limit the number of worker tasks so that they don't saturate your web service, then I think your approach is fine.

Long Answer: The new System.Threading.Tasks engine in .NET 4.0 runs on top of the .NET ThreadPool. Since there is only ever one ThreadPool per process and defaults to a maximum of 250 worker threads. Therefore, if you were to set the ThreadPool's maximum number of threads to a more modest number, you may be able to reduce the number of concurrently executing threads, and thus tasks using the ThreadPool.SetMaxThreads (...) API.

HOWEVER, note that you may well not be alone in utilizing the ThreadPool as many other classes that you utilize may also queue items to the ThreadPool. Therefore, there's a good chance that you may end up crippling the rest of your app by doing this. Also note that because the ThreadPool employs an algorithm to optimize its use of a given machine's underlying cores, limiting the number of threads the threadpool can queue to an arbitrarily low number can result in some pretty catastrophic performance issues.

Again, if you want to execute a small number of worker tasks/threads to exercise some task, then only creating a small number of tasks (vs. 100's) is the best approach.

bitcrazed
A: 

I know this is an old thread, but I just wanted to share my solution to this problem: use semaphores.

(This is in C#)

private void RunAllActions(IEnumerable<Action> actions, int maxConcurrency)
{
    using(SemaphoreSlim concurrencySemaphore = new SemaphoreSlim(maxConcurrency))
    {
        foreach(Action action in actions)
        {
            Task.Factory.StartNew(() =>
            {
                concurrencySemaphore.Wait();
                try
                {
                    action();
                }
                finally
                {
                    concurrencySemaphore.Release();
                }
            });
        }
    }
}
Arrow_Raider
Thanks Arrow_Raider. This is a much better solution. I implemented this, but used a "continuation task" to handle the semaphore release.
James