views:

71

answers:

3

Let's say I have a

List<Thread> df = new List<Thread>();
// I add 500 instances of delegate() { somemethod(a,b) }; to df

Now I want to run through all df items threading it X items maximum at a time, how can I accomplish this?

+2  A: 

Rather than a List of Threads, look at ThreadPool: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx. In a ThreadPool, the number of threads in the pool is your "X concurrent items" and you can use ThreadPool.QueueUserWorkItem(...); for each of your delegates.

Scott Stafford
+1  A: 

Take a look at this: http://www.albahari.com/threading/part2.aspx#_ProducerConsumerQWaitHandle

It's a sample chapter from the book "C# in a Nutshell". It will benefit you to read the entire chapter, but you can focus in on the section I gave the link for: Producer and Consumer Queues

Specifically, read this excerpt:

"Another common threading scenario is to have a background worker process tasks from a queue. This is called a Producer/Consumer queue: the producer enqueues tasks; the consumer dequeues tasks on a worker thread. ... A Producer/Consumer queue is scaleable, in that multiple consumers can be created – each servicing the same queue, but on a separate thread."

If you create a Producer/Consumer Queue, like the example featured in the chapter, you can create multiple instances of the consumer. That way, each consumer will dequeue a delegate off the queue.

SimpleCoder
+4  A: 

There are a variety of options at your disposal. Depending on what framework you have access to, the options may differ. At your disposal through .NET 4, you have: Thread class w/ Signals, ThreadPool, Asynchronous Delegate Invocation, Task Parallel Library.

I'll run through a couple of your options here, and you can pick the one that best fits your needs.

Threading and Signaling

The first option is the most manual. You would spin up instances of the Thread class up to your threshold X, and whenever a thread completes, spin up another thread.

Example

public void ProcessDelegates(IList<Action> thingsToDo, int maxConcurrency)
{
    int currentConcurrency = 0;
    var autoevent = new AutoResetEvent(false);
    foreach (var thingToDo in thingsToDo)
    {
        var thread = new Thread(
            () =>
            {
                thingToDo();
                autoevent.Set();
            }
        );

        if (++currentConcurrency >= maxConcurrency)
        {
            autoevent.WaitOne();
            --currentConcurrency;
        }
    }
}

ThreadPool

As an alternative to using a List, you can queue worker items in the ThreadPool, and let it work through them at its leisure. Be wary of using the ThreadPool to process a large amount of concurrent work. There is ONE thread pool per AppDomain, which defaults to about 25 concurrent threads. Those threads must be available to handle ALL threads in that app domain, including things the runtime may do in the background. ThreadPool should only be used for a small amount of concurrent work items.

Example

ThreadPool.QueueUserWorkItem(
    data =>
    {
        thingToDo(a, b);
    }
);

Asynchronous Delegate Invocation

Since you are using delegates, it is possible to use asynchronous invocation to implement threading. This is a more abstracted approach to multithreading, and sometimes easier to manage. In this case, it is very similar to starting your own threads manually. It should be noted that Delegate.BeginInvoke uses the ThreadPool internally, and the same rules that apply to using ThreadPool itself apply here as well.

Example

public void ProcessDelegates(IList<Action> thingsToDo, int maxConcurrency)
{
    int currentConcurrency = 0;        
    IList<WaitHandle> resultHandles = new List<WaitHandle>();

    foreach (var thingToDo in thingsToDo)
    {
        var asyncResult = thingToDo.BeginInvoke();
        resultHandles.Add(asyncResult.AsyncWaitHandle);

        if (++currentConcurrency >= maxConcurrency)
        {
            WaitHandle.WaitAny(resultHandles.ToArray());
            --currentConcurrency;
        }
    }
}

.NET 4 Task Parallel Library

If you have access to .NET 4, you could also try using the Task Parallel Library. Task parallelism is a newer approach to multithreading in .NET 4 that offers simple use and very rich runtime debugging capabilities.

In the example below, the TaskCreationOptions.PreferFairness tells the task parallel library to attempt to let tasks started earlier complete earlier, resulting in generally sequential execution of tasks.

Example

public void ProcessDelegates(IList<Action> thingsToDo)
{
    var tasks = thingsToDo.Select(
        ttd => Task.Factory.StartNew(ttd, TaskCreationOptions.PreferFairness)
    ).ToArray();

    Task.WaitAll(tasks);
}

Disclaimer: These examples are off the top of my head, and explicitly simplistic, so I make no guarantees about their perfect stability in a real-world scenario. I recommend using these as a basis to implement threading that is appropriate for whatever application you apply them to.

jrista
A very thoughtful reply.
ebpower