С# Ожидание завершения нескольких потоков
У меня есть приложение windows forms, которое я проверяю на всех последовательных портах, чтобы узнать, подключено ли какое-либо устройство.
Вот как я выкручиваю каждый поток. Следующий код уже выделен из основного потока 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
там, но это просто обрабатывает их линейно.
Ответы
Ответ 1
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
Я оглядывался назад, и мне нравится следующее лучше
availPorts.Select(ser =>
{
Thread thread = new Thread(lookForValidDev);
thread.Start(ser);
return thread;
}).ToList().ForEach(t => t.Join());
Ответ 2
Используйте классы 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();
}
}
Ответ 3
Самый простой и безопасный способ сделать это - использовать CountdownEvent. См. Albahari.
Ответ 4
Вы можете использовать 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();
}
Ответ 5
Сохраняйте результаты Thread в списке после того, как они были порождены, и повторите этот список - во время итерационного вызова join. Вы все равно присоединяетесь линейно, но должны делать то, что хотите.