C#等待多个线程完成

42

我有一个Windows窗体应用程序,我正在检查所有串口以查看是否连接了特定设备。

以下是如何启动每个线程。下面的代码已经从主GUI线程中分离出来。

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

我希望下一行代码能等待所有线程完成。我尝试在那里使用t.join,但那只会按顺序处理它们。


2
严格作为一则旁注,而不是你询问的内容,但你可以在线程上设置IsBackground = true,以避免在退出应用程序时阻塞主线程。 - Patrick
5个回答

49
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();
}

编辑

我回顾了一下这个内容,我更喜欢以下的表述:

availPorts.Select(ser =>
      {
          Thread thread = new Thread(lookForValidDev);
          thread.Start(ser);
          return thread;
      }).ToList().ForEach(t => t.Join());

这是一个好的、简单的解决方案。但在我的情况下,System.Threading.Thread.Join() 在仪器分析报告中可见,并具有高独占时间值。尽管如此,这确实是一种非常好的方法。 - Raphoq

16

使用AutoResetEvent和ManualResetEvent类:

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();
    }


} 

2
auto.Set() 应该在 finally 块中。 - Robert Davis

10

最简单、最安全的方法是使用CountdownEvent。请参见Albahari


啊,我不知道如果线程已经终止,Join 就会返回。感谢您的更正。 - IamIC

3

在线程被启动后,将其结果存储在列表中,并在迭代期间调用join。你仍然按顺序执行join,但它应该实现你想要的效果。


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();
    }
}

使用示例:

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();
}

这不是在.NET中作为CountdownEvent包含吗?https://msdn.microsoft.com/zh-cn/library/system.threading.countdownevent(v=vs.110).aspx - Redwood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接