我正在尝试使用ThreadPool.RegisterWaitForSingleObject将计时器添加到一组线程中。我创建了9个线程,并且我试图给每个线程提供相同的操作机会,因为如果我只是将它们添加到线程池中,那么似乎会有一点饥饿。我也在尝试实现手动重置事件,因为我想在继续之前退出所有9个线程。
确保线程池中的每个线程都有相同的运行机会的最佳方法是什么,因为我调用的函数有一个循环,似乎每个线程(或者先运行的任何线程)都卡在其中其他人没有机会跑。
resetEvents = new ManualResetEvent[table_seats];
//Spawn 9 threads
for (int i = 0; i < table_seats; i++)
{
resetEvents[i] = new ManualResetEvent(false);
//AutoResetEvent ev = new AutoResetEvent(false);
RegisteredWaitHandle handle = ThreadPool.RegisterWaitForSingleObject(autoEvent, ObserveSeat, (object)i, 100, false);
}
//wait for threads to exit
WaitHandle.WaitAll(resetEvents);
但是,如果我使用resetEvents []或ev似乎无法正常工作并不重要。我能够实现这一点,还是我(可能)误解了它们应该如何工作。
谢谢,R。
答案 0 :(得分:4)
我不会将RegisterWaitForSingleObject
用于此目的。我将在这里描述的模式需要Reactive Extensions下载,因为您使用的是.NET v3.5。
首先,等待ThreadPool
中的所有工作项完成使用CountdownEvent
课程。与使用多个ManualResetEvent
实例相比,这更加优雅和可扩展。另外,WaitHandle.WaitAll
方法仅限于64个句柄。
var finished = new CountdownEvent(1);
for (int i = 0; i < table_seats; i++)
{
finished.AddCount();
ThreadPool.QueueUserWorkItem(ObserveSeat);
(state) =>
{
try
{
ObserveSeat(state);
}
finally
{
finished.Signal();
}
}, i);
}
finished.Signal();
finished.Wait();
其次,你可以尝试在循环的几次迭代之后调用Thread.Sleep(0)
来强制进行上下文切换,以便当前线程产生另一个。如果您需要更复杂的协调策略,请使用Barrier
类。将另一个参数添加到接受此同步机制的ObserveSeat
函数中。您可以通过在上面的代码中的lambda表达式中捕获它来提供它。
public void ObserveSeat(object state, Barrier barrier)
{
barrier.AddParticipant();
try
{
for (int i = 0; i < NUM_ITERATIONS; i++)
{
if (i % AMOUNT == 0)
{
// Let the other threads know we are done with this phase and wait
// for them to catch up.
barrier.SignalAndWait();
}
// Perform your work here.
}
}
finally
{
barrier.RemoveParticipant();
}
}
请注意,尽管这种方法肯定会阻止饥饿问题,但它可能会限制线程的吞吐量。调用SignalAndWait
太多可能会导致大量不必要的上下文切换,但调用它太少可能会导致大量不必要的等待。您可能需要调整AMOUNT
以获得吞吐量和饥饿的最佳平衡。我怀疑可能有一种简单的方法可以动态调整。