Saint
Saint

Reputation: 5469

For loop in many threads

How can I run each call for loop in another thread, but continuation of ExternalMethod should wait to ending of last working thread from for loop (and synchronize) ?

ExternalMethod()
{
    //some calculations
    for (int i = 0; i < 10; i++)
    {
        SomeMethod(i);
    }
    //continuation ExternalMethod
}

Upvotes: 2

Views: 3692

Answers (4)

Brian Gideon
Brian Gideon

Reputation: 48949

One approach is to use a CountdownEvent.

ExternalMethod()
{
    //some calculations
    var finished = new CountdownEvent(1);
    for (int i = 0; i < 10; i++)
    {
        int capture = i; // This is needed to capture the loop variable correctly.
        finished.AddCount();
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              SomeMethod(capture);
            }
            finally
            {
              finished.Signal();
            }
          }, null);
    }
    finished.Signal();
    finished.Wait();
    //continuation ExternalMethod
}

If CountdownEvent is not available then here is an alternate approach.

ExternalMethod()
{
    //some calculations
    var finished = new ManualResetEvent(false);
    int pending = 1;
    for (int i = 0; i < 10; i++)
    {
        int capture = i; // This is needed to capture the loop variable correctly.
        Interlocked.Increment(ref pending);
        ThreadPool.QueueUserWorkItem(
          (state) =>
          {
            try
            {
              SomeMethod(capture);
            }
            finally
            {
              if (Interlocked.Decrement(ref pending) == 0) finished.Set();
            }
          }, null);
    }
    if (Interlocked.Decrement(ref pending) == 0) finished.Set();
    finished.WaitOne();
    //continuation ExternalMethod
}

Note that in both examples the for loop itself is treating as a parallel work item (it is on a separate thread from the other work items afterall) to avoid a really subtle race condition that might occur if the first work item signals the event before the next work item is queued.

Upvotes: 1

seairth
seairth

Reputation: 2062

For .NET 3.5, maybe something like this:

Thread[] threads = new Thread[10];

for (int x = 0; x < 10; x++)
{
    threads[x] = new Thread(new ParameterizedThreadStart(ThreadFun));
    threads[x].Start(x);
}

foreach (Thread thread in threads) thread.Join();

It may seem counterintuitive to use the Join() method, but since you are effectively doing a WaitAll-type pattern, it doesn't matter what order the joins are executed.

Upvotes: 0

Rudi Visser
Rudi Visser

Reputation: 21979

One approach would be to use a ManualResetEvent.

Consider the following code (note that this should not be taken as a working example, stuck on OSX so don't have VS nor a C# compiler to hand to check this over):

static ManualResetEvent mre = new ManualResetEvent(false);
static int DoneCount = 0;
static int DoneRequired = 9;
void ExternalMethod() {
        mre.Reset();
        for (int i = 0; i < 10; i++) {
                new Thread(new ThreadStart(ThreadVoid)).Start();
        }
        mre.WaitOne();
}

void ThreadVoid() {
        Interlocked.Increment(ref DoneCount);   

        if (DoneCount == DoneRequired) {
                mre.Set();
        }
}

IMPORTANT - This possibly isn't the best way to do it, just an example of using ManualResetEvent, and it will suit your needs perfectly fine.

If you're on .NET 4.0 you can use a Parallel.For loop - explained here.

Upvotes: 5

seairth
seairth

Reputation: 2062

System.Threading.Tasks.Parallel.For(0, 10, (i) => SomeMethod(i));

Upvotes: 4

Related Questions