如何使用C#使线程按顺序通过门

我有三个线程,部分代码可以并行运行,一些部分被锁定(当时只有一个线程)。 但是一个锁需要只按顺序让它们进入。 由于这是一个循环,它变得更加复杂。 我该如何表现这种行为?

如果我有一个印刷声明我想收到以下输出:1,2,3,1,2,3,1,2,3 ….目前我收到2,3,1,3,1,3 ,2,1,2 AKA随机顺序。

在三个线程中并行执行的代码:

while (true){ lock (fetchLock){ if(done){ break; } //Do stuff one at the time } //Do stuff in parralell lock (displayLock){ //Do stuff one at the time but need's to be in order. } } 

您可以使用BarrierAutoResetEvent的组合来实现此目的。

首先,使用Barrier.SignalAndWait()确保所有线程在继续之前到达公共点。 这个共同点是您希望线程按顺序执行某些代码的点。

然后使用numberOfThreads-1 AutoResetEvents来同步线程。 第一个线程不需要等待任何其他线程,但在完成后它应该发出下一个线程正在等待的事件的信号。

中间线程(或线程,如果总共超过3个线程)需要等待前一个线程发出告知它继续进行的事件的信号。 完成后,中间线程应该发出下一个线程正在等待的事件的信号。

最后一个线程需要等待前一个线程发出告知它继续进行的事件的信号。 因为它是最后一个线程,所以它不需要发出事件信号来告诉下一个线程继续。

最后,通过对Barrier.SignalAndWait()另一次调用重新同步线程。

这最容易通过示例控制台应用程序显示。 如果你运行它,你会看到线程应该按顺序完成的工作(在输出中以字母“B”为前缀)确实总是按顺序进行,而另一项工作(带有字母“A”的前缀) “)以随机顺序执行。

 using System; using System.Threading; using System.Threading.Tasks; namespace Demo { public static class Program { public static void Main() { using (Barrier barrier = new Barrier(3)) using (AutoResetEvent t2 = new AutoResetEvent(false)) using (AutoResetEvent t3 = new AutoResetEvent(false)) { Parallel.Invoke ( () => worker(1, barrier, null, t2), () => worker(2, barrier, t2, t3), () => worker(3, barrier, t3, null) ); } } private static void worker(int threadId, Barrier barrier, AutoResetEvent thisThreadEvent, AutoResetEvent nextThreadEvent) { Random rng = new Random(threadId); for (int i = 0; i < 1000; ++i) { doSomething(threadId, rng); // We don't care what order threads execute this code. barrier.SignalAndWait(); // Wait for all threads to reach this point. if (thisThreadEvent != null) // If this thread is supposed to wait for a signal thisThreadEvent.WaitOne(); // before proceeding, then wait for it. doWorkThatMustBeDoneInThreadOrder(threadId); if (nextThreadEvent != null) // If this thread is supposed to raise a signal to indicate nextThreadEvent.Set(); // that the next thread should proceed, then raise it. barrier.SignalAndWait(); // Wait for all threads to reach this point. } } private static void doWorkThatMustBeDoneInThreadOrder(int threadId) { Console.WriteLine(" B" + threadId); Thread.Sleep(200); // Simulate work. } private static void doSomething(int threadId, Random rng) { for (int i = 0; i < 5; ++i) { Thread.Sleep(rng.Next(50)); // Simulate indeterminate amount of work. Console.WriteLine("A" + threadId); } } } }