实现ThreadPool.RegisterWaitForSingleObject的正确方法

时间:2010-09-23 01:00:39

标签: c# multithreading visual-studio-2008

我正在尝试使用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。

1 个答案:

答案 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以获得吞吐量和饥饿的最佳平衡。我怀疑可能有一种简单的方法可以动态调整。