我正在尝试使用 CountdownEvent 仅允许线程在事件计数为零时继续,但我希望初始计数为零。实际上,我希望返回零行为,每当计数为零时,就会发出事件信号,并且只要线程大于零,就会使线程等待。
我可以使用0初始计数初始化Countdown事件,但是当我尝试添加到计数时,我得到 InvalidOperationException “CountdownEvent_Increment_AlreadyZero”。
是否有替代类或其他方式我可以使用倒计时事件以避免此限制?
答案 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)
信号量如何:http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx
编辑:以下文章讨论了为什么不推荐您所描述的内容,并建议解决方法:http://social.msdn.microsoft.com/Forums/en/parallelextensions/thread/aa49f92c-01a8-4901-9846-91bc1587f3ae
答案 6 :(得分:0)
您可以使用队列对象将“工作”添加到其中并再次取出。
只有当队列为空时,您才会继续前进。
但是,我们在这里需要具体细节......
答案 7 :(得分:0)
我遇到了相同的issue,但是遇到的是Barrier
。
实际上,如果您考虑一下,CountdownEvent
是一个阶段Barrier
。
因此,为了避免以下限制:
我可以使用0的初始计数初始化Countdown事件,但是当我尝试 添加到计数我得到InvalidOperationException “ CountdownEvent_Increment_AlreadyZero”。
您可以继续使用Barrier
并使用其AddParticipant
或RemoveParticipan
方法。