2010-02-17 189 views
25

我有一个Windows窗体应用程序,我正在检查所有串行端口,以查看特定设备是否连接。C#等待多个线程完成

这是我如何分离每个线程。下面的代码已经脱离了主要的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 2010-02-17 15:43:14

回答

34
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()); 
+0

这是一个很好且简单的解决方案。但在我的情况下,System.Threading.Thread.Join()在具有高排他时间值的检测分析报告中可见。尽管如此,这是一个非常棒的方式。 – Raph 2016-12-22 09:04:02

2

存放在列表中的线程结果后,他们催生和迭代名单 - 在迭代期间调用加入。你仍然线性地加入,但它应该做你想要的。

+0

任何完整的源代码? – Kiquenet 2014-01-01 09:51:12

14

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


} 
+0

看起来非常棒! – abatishchev 2010-02-17 17:38:46

+2

auto.Set()应该在finally块中 – 2010-02-17 22:43:33

+3

ManualResetEvent在这里有什么意义? – 2013-08-21 21:46:43

3

可以使用的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(); 
} 
+0

这是不是包含在.NET中作为CountdownEvent? https://msdn.microsoft.com/en-us/library/system.threading.countdownevent(v=vs.110).aspx – 2015-12-03 00:09:53

5

以最简单和最安全的方式这样做是为了使用CountdownEvent。请参阅Albahari

+0

啊,我不知道如果线程已经终止,Join会返回。感谢您的更正。 – IamIC 2013-07-12 16:24:09