如何使用C#使线程按顺序通过门
How to make threads go through a gate in order using C#
我有三个线程,部分代码可以运行并行,有些部分被锁定(当时只有一个线程)。然而,一把锁只需要让他们按顺序进入。由于这是一个循环,它变得更加复杂。我该如何做出这种行为?
如果我有一个打印语句,我希望收到以下输出:
1,2,3,1,2,3,1,2,3.... 目前我收到 2,3,1,3,1,3,2,1,2 A.K.A。随机顺序。
三个线程并行执行的代码:
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.
}
}
您可以使用 Barrier
and AutoResetEvent
的组合来实现此目的。
首先,您使用 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);
}
}
}
}
我有三个线程,部分代码可以运行并行,有些部分被锁定(当时只有一个线程)。然而,一把锁只需要让他们按顺序进入。由于这是一个循环,它变得更加复杂。我该如何做出这种行为?
如果我有一个打印语句,我希望收到以下输出: 1,2,3,1,2,3,1,2,3.... 目前我收到 2,3,1,3,1,3,2,1,2 A.K.A。随机顺序。
三个线程并行执行的代码:
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.
}
}
您可以使用 Barrier
and AutoResetEvent
的组合来实现此目的。
首先,您使用 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);
}
}
}
}