CountdownEvent返回零

时间:2010-12-08 13:39:40

标签: c# .net multithreading countdownevent

我正在尝试使用 CountdownEvent 仅允许线程在事件计数为零时继续,但我希望初始计数为零。实际上,我希望返回零行为,每当计数为零时,就会发出事件信号,并且只要线程大于零,就会使线程等待。

我可以使用0初始计数初始化Countdown事件,但是当我尝试添加到计数时,我得到 InvalidOperationException “CountdownEvent_Increment_AlreadyZero”。

是否有替代类或其他方式我可以使用倒计时事件以避免此限制?

8 个答案:

答案 0 :(得分:6)

您写道:

  

我正在执行一项操作,该操作将创建未知数量的子操作(不是任务或线程)

那他们是什么?你应该这样做:

CountdownEvent ev;  
public void foo() {
    ev = new CountdownEvent(1);
    foreach ( <task in tasks_to_start> ) {
         ev.AddCount();
         // enter code here which starts your task
    }
    ev.Signal();
    ev.Wait();
}

public static void youtTask(CountdownEvent ev) {
    // some work
    // ...
    // after all is done
    ev.Signal();
}

答案 1 :(得分:3)

如果可以将.NET 4.0或Reactive Extensions用于.NET 3.5(具有.NET 4 TPL功能的后端),则可以查看Barrier类。它允许您协调多个并行任务,以便它们不会持续,直到障碍中的所有参与者都已发出信号到达。它还应满足您在处理过程中让参与者出现和消失的要求。

答案 2 :(得分:2)

所以从本质上讲,你需要一个“开/关开关”,而不是一个可以设置任意倒计时的同步对象。 CountdownEvent不适用于此类案件。

为什么不使用初始计数为1的Semaphore

答案 3 :(得分:1)

这对你有用吗? http://msdn.microsoft.com/en-us/library/dd384749.aspx

修改
抱歉,这很模糊。使用SOReader的答案,其中每个父级的倒计时事件从1开始 - 然后在子级中使用TryAddCount递增,然后将父级递减到1,然后在子级完成时从父级递减1到0,最后递减计数父线程的父级。这是一系列树状的倒计时。

我对多线程没有经验,但初看起来就是我会尝试的。

答案 4 :(得分:1)

您的问题似乎是一种常见的树木行走分叉技术。每次进行递归时,您都会启动另一个并发操作(将其排入线程池等)。但是你需要等待所有的子支柱在最后完成。只需为您启动的每个子操作向倒计时事件添加1,并在每个子操作结束时发出信号。只要你安排算法就行是安全的,这样它就不会发出信号,直到它为每个子操作添加它。

我应该补充一点,你需要知道前面的计数,只需在根处设置1,每次分叉到一个孩子时,加1,然后发信号每一个的末尾,它将动态处理任何没有前期成本的树。

CountdownEvent有一个方法Add,可让你增加飞行中的计数。

这有意义吗?我可能会偏离你想要完成的任务。

但是,如果你真的想要一个与你指定的方式相同的CountdownEvent,那么很容易在一个类中包含几个互锁操作来做你所说的。

然而,CountdownEvent的构造是羽毛重量,如果没有人在发出信号之前等待,它几乎是免费的。在昂贵的情况下,它是最佳的,无论有多少任务(等),它只需要将一个内核转换为信号,一个等待,最坏的情况。

要实现您的建议,需要围绕信号的重新同步和事件的重置。倒计时事件依赖于一个简单的原则,只有在信号呼叫中从非零转换为零才可能发出事件信号。没有竞争,因为多个线程不可能一次更改值(它是互锁的),因此只有一个线程可以尝试发信号通知事件对象(唤醒其他等待线程)。完美。

但是,如果您有多个线程设置并重置它,您需要在设置和重置周围同步,因为计数可能会抖动几次,并且多个线程将同时尝试设置或重置事件。 (设置,重置和等待事件都很昂贵,因为它们都必须进行内核转换并导致上下文切换)。除非您同步某些内容以保护设置/重置转换,否则它将无法工作。如果他们将它添加到CountdownEvent它将不再是最佳的,它将显着更昂贵。

答案 5 :(得分:0)

答案 6 :(得分:0)

您可以使用队列对象将“工作”添加到其中并再次取出。

只有当队列为空时,您才会继续前进。

但是,我们在这里需要具体细节......

答案 7 :(得分:0)

我遇到了相同的issue,但是遇到的是Barrier

实际上,如果您考虑一下,CountdownEvent是一个阶段Barrier

因此,为了避免以下限制:

  

我可以使用0的初始计数初始化Countdown事件,但是当我尝试   添加到计数我得到InvalidOperationException   “ CountdownEvent_Increment_AlreadyZero”。

您可以继续使用Barrier并使用其AddParticipantRemoveParticipan方法。