41

I have a windows forms app that I am checking all the serial ports to see if a particular device is connected.

This is how I spin off each thread. The below code is already spun off the main gui thread.

foreach (cpsComms.cpsSerial ser in availPorts)
{
    Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));
    t.Start((object)ser);//start thread and pass it the port
}

I want the next line of code to wait until all the threads have finished. I've tried using a t.join in there, but that just processes them linearly.

Rob
  • 45,296
  • 24
  • 122
  • 150
Andy
  • 44,610
  • 13
  • 70
  • 69
  • 2
    Strictly as a side note and not that you asked about it, but you can put IsBackground = true on the thread to not have it block the main thread if you exit the application. – Patrick Feb 17 '10 at 15:43

5 Answers5

48
List<Thread> threads = new List<Thread>();
foreach (cpsComms.cpsSerial ser in availPorts)
{
    Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));
    t.Start((object)ser);//start thread and pass it the port
    threads.Add(t);
}
foreach(var thread in threads)
{
    thread.Join();
}

Edit

I was looking back at this, and I like the following better

availPorts.Select(ser =>
      {
          Thread thread = new Thread(lookForValidDev);
          thread.Start(ser);
          return thread;
      }).ToList().ForEach(t => t.Join());
Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142
  • This is good and simple solution. But in my case System.Threading.Thread.Join() is visible in instrumentation profiling report with high exclusive time value. Despite of this, that is a really great way. – Raphoq Dec 22 '16 at 09:04
16

Use the AutoResetEvent and ManualResetEvent Classes:

private ManualResetEvent manual = new ManualResetEvent(false);
void Main(string[] args)
{
    AutoResetEvent[] autos = new AutoResetEvent[availPorts.Count];

    manual.Set();

    for (int i = 0; i < availPorts.Count - 1; i++)
        {

        AutoResetEvent Auto = new AutoResetEvent(false);
        autos[i] = Auto;

        Thread t = new Thread(() => lookForValidDev(Auto, (object)availPorts[i]));
        t.Start();//start thread and pass it the port  

    }
    WaitHandle.WaitAll(autos);
    manual.Reset();

}


void lookForValidDev(AutoResetEvent auto, object obj)
{
    try
    {
         manual.WaitOne();
         // do something with obj 
    }
    catch (Exception)
    {

    }
    finally
    {
        auto.Set();
    }


} 
Islam Yahiatene
  • 1,441
  • 14
  • 27
10

The simplest and safest way to do this is to use a CountdownEvent. See Albahari.

IamIC
  • 17,747
  • 20
  • 91
  • 154
  • Ah, I didn't know that if the thread has terminated, the Join would return. Thanks for the correction. – IamIC Jul 12 '13 at 16:24
2

Store the Thread results in a list after they were spawned and iterate the list - during iteration call join then. You still join linearly, but it should do what you want.

NG.
  • 22,560
  • 5
  • 55
  • 61
2

You can use a CountDownLatch:

public class CountDownLatch
{
    private int m_remain;
    private EventWaitHandle m_event;

    public CountDownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}

Example how to use it:

void StartThreads
{
    CountDownLatch latch = new CountDownLatch(availPorts.Count);

    foreach (cpsComms.cpsSerial ser in availPorts)
    {
        Thread t = new Thread(new ParameterizedThreadStart(lookForValidDev));

        //start thread and pass it the port and the latch
        t.Start((object)new Pair(ser, latch));

    }

    DoSomeWork();

    // wait for all the threads to signal
    latch.Wait();

    DoSomeMoreWork();
}

// In each thread
void NameOfRunMethod
{
    while(running)
    {
        // do work
    }

    // Signal that the thread is done running
    latch.Signal();
}
Kiril
  • 39,672
  • 31
  • 167
  • 226
  • Isn't this included in .NET as CountdownEvent? https://msdn.microsoft.com/en-us/library/system.threading.countdownevent(v=vs.110).aspx – Redwood Dec 03 '15 at 00:09