屏障 (.NET Framework)
关卡是一个用户定义的同步基元,它使多个线程(称为“参与者”)可以分阶段同时参与执行一个算法。 每个参与者将执行到代码中的关卡点为止。 关卡表示一个阶段的工作结束。 当某个参与者到达关卡时,它将阻塞,直至所有参与者都已到达同一关卡为止。 在所有参与者都已到达关卡之后,您可以选择调用后期阶段操作。 在所有其他线程仍被阻塞时,单个线程可以使用此后期阶段操作来执行操作。 在执行该操作之后,所有参与者都将被取消阻塞。
下面的代码段演示了基本关卡模式。
' Create the Barrier object, and supply a post-phase delegate
' to be invoked at the end of each phase.
Dim barrier = New Barrier(2, Sub(bar)
' Examine results from all threads, determine
' whether to continue, create inputs for next phase, etc.
If (someCondition) Then
success = True
End If
End Sub)
' Define the work that each thread will perform. (Threads do not
' have to all execute the same method.)
Sub CrunchNumbers(ByVal partitionNum As Integer)
' Up to System.Int64.MaxValue phases are supported. We assume
' in this code that the problem will be solved before that.
While (success = False)
' Begin phase:
' Process data here on each thread, and optionally
' store results, for example:
results(partitionNum) = ProcessData(myData(partitionNum))
' End phase:
' After all threads arrive,post-phase delegate
' is invoked, then threads are unblocked. Overloads
' accept a timeout value and/or CancellationToken.
barrier.SignalAndWait()
End While
End Sub
' Perform n tasks to run in in parallel. For simplicity
' all threads execute the same method in this example.
Shared Sub Main()
Dim app = New BarrierDemo()
Dim t1 = New Thread(Sub() app.CrunchNumbers(0))
Dim t2 = New Thread(Sub() app.CrunchNumbers(1))
t1.Start()
t2.Start()
End Sub
// Create the Barrier object, and supply a post-phase delegate
// to be invoked at the end of each phase.
Barrier barrier = new Barrier(2, (bar) =>
{
// Examine results from all threads, determine
// whether to continue, create inputs for next phase, etc.
if (someCondition)
success = true;
});
// Define the work that each thread will perform. (Threads do not
// have to all execute the same method.)
void CrunchNumbers(int partitionNum)
{
// Up to System.Int64.MaxValue phases are supported. We assume
// in this code that the problem will be solved before that.
while (success == false)
{
// Begin phase:
// Process data here on each thread, and optionally
// store results, for example:
results[partitionNum] = ProcessData(data[partitionNum]);
// End phase:
// After all threads arrive,post-phase delegate
// is invoked, then threads are unblocked. Overloads
// accept a timeout value and/or CancellationToken.
barrier.SignalAndWait();
}
}
// Perform n tasks to run in in parallel. For simplicity
// all threads execute the same method in this example.
static void Main()
{
var app = new BarrierDemo();
Thread t1 = new Thread(() => app.CrunchNumbers(0));
Thread t2 = new Thread(() => app.CrunchNumbers(1));
t1.Start();
t2.Start();
}
有关完整示例,请参见如何:使用屏障来使并发操作保持同步。
添加和移除参与者
创建 Barrier 时,指定参与者的数量。 也可在任何时间动态地添加或移除参与者。 例如,如果一个参与者解决了自己的问题部分,则可以存储结果,停止执行该线程并调用 RemoveParticipant 以减少关卡中的参与者的数量。 当通过调用 AddParticipant 来添加参与者时,返回值会指定当前的阶段编号,这对于初始化新的参与者的工作可能会有用。
出现故障的关卡
如果一个参与者未能到达关卡,则会发生死锁。 若要避免这些死锁,请使用 SignalAndWait 方法的重载来指定超时期限和取消标记。 这些重载将返回一个布尔值,每个参与者都会在它继续下一阶段之前检查该值。
后期阶段异常
如果后期阶段委托引发异常,则在 BarrierPostPhaseException 对象中包装它,然后将其传播到所有参与者。
关卡与 ContinueWhenAll
当线程循环执行多个阶段时,关卡尤其有用。 如果您的代码只需要一个或两个阶段的工作,请考虑是否使用带有各种隐式联接的 System.Threading.Tasks.Task 对象,包括:
有关更多信息,请参见如何:用延续将多个任务链接在一起。