在c#.Net 3.5中,如何控制多个WaitHandles?

时间:2011-05-19 15:08:45

标签: multithreading .net-3.5 synchronization asp.net-3.5

我有以下情况:

我的应用程序必须从另一个应用程序导入几组数据,时间至关重要 因此,它为每次导入生成一个线程 所以,假设我通过10进口1,其中进口4和5只能在导入后导入1,6和7,导入后导入8,9和10 3

  • 导入1
    • 导入4
    • 导入5
  • 导入2
    • 导入6
    • 导入7
  • 导入3
    • 导入8
    • 导入9
    • 导入10

搜索stackoverflow我找到了一些关于等待句的答案,但我不确定控制多个等待句柄。
我想创建一个导入/句柄列表并在循环中检查它们,但我不知道如何从那里触发.set()。
或者为每个父导入创建一个线程并实例化其中的句柄,并且该父线程为每个子导入触发一个线程,但处理线程的线程是我不确定是正确的。

关于如何解决这个问题的任何想法?

更新:
在Brian Gideon的回答之后发生了我想出的事情:
dateSince = Convert.ToDateTime(txtBoxDateSince.Text); dateTo = Convert.ToDateTime(txtBoxDateTo.Text);

//Loop all the days on the time interval while (DateTime.Compare(dateSince, dateTo) <= 0) {

foreach (ListItem father in checkBoxListFather.Items)
{
    if (father.Selected == true)
    {
        processClass process = new processClass();

        // This WaitHandle will be used to get the child tasks going.
        var wh = new ManualResetEvent(false);

        //Method to Import, wraped in a delegate
        WaitCallback fatherMethod = new WaitCallback(process.importProcess);
        //and its parameters
        processClass.importParameters param = new processClass.importParameters(wh, father.Value, null, dateSince);

        // Queue the parent task.
        ThreadPool.QueueUserWorkItem(fundMethod, param);

        // Register the child tasks.
        foreach (ListItem child in checkBoxListChild.Items)
        {
            if (child.Selected == true)
            {
                processClass.importParameters parameters = new processClass.importParameters(null, child.Value, null, dateSince);

                // Registers a callback for the child task that will execute once the
                // parent task is complete.
                WaitOrTimerCallback childMethod = new WaitOrTimerCallback(process.anotherImportProcess);
                RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh, childMethod, parameters, Timeout.Infinite, true);

            }//End if (child.Selected == true)

        }//End foreach (ListItem fund in checkBoxListChild.Items)

    }//End if (father.Selected == true)

}//End foreach (ListItem fundProcess in checkBoxListFather.Items)

dateSince = dtSince.AddDays(1);

foreach (ListItem father in checkBoxListFather.Items) { if (father.Selected == true) { processClass process = new processClass(); // This WaitHandle will be used to get the child tasks going. var wh = new ManualResetEvent(false); //Method to Import, wraped in a delegate WaitCallback fatherMethod = new WaitCallback(process.importProcess); //and its parameters processClass.importParameters param = new processClass.importParameters(wh, father.Value, null, dateSince); // Queue the parent task. ThreadPool.QueueUserWorkItem(fundMethod, param); // Register the child tasks. foreach (ListItem child in checkBoxListChild.Items) { if (child.Selected == true) { processClass.importParameters parameters = new processClass.importParameters(null, child.Value, null, dateSince); // Registers a callback for the child task that will execute once the // parent task is complete. WaitOrTimerCallback childMethod = new WaitOrTimerCallback(process.anotherImportProcess); RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh, childMethod, parameters, Timeout.Infinite, true); }//End if (child.Selected == true) }//End foreach (ListItem fund in checkBoxListChild.Items) }//End if (father.Selected == true) }//End foreach (ListItem fundProcess in checkBoxListFather.Items) dateSince = dtSince.AddDays(1);

几乎相同的答案,只使用没有lambda表达式的方法和使用它们的参数 我仍然没有对它进行压力测试,但它的工作非常好。

谢谢Brian。

2 个答案:

答案 0 :(得分:2)

你知道吗

  1. WaitHandle.WaitAny Method
  2. WaitHandle.WaitAll Method
  3. 如果线程本质上是简单线性的,并且您不需要排队以获得可伸缩性,那么您可以使用C#: Waiting for all threads to complete

答案 1 :(得分:1)

如果你真的想为每个任务专门设置一个线程,则根本不需要使用WaitHandle个实例。 1 你可以传递Thread个实例,正在为每个子任务执行父任务并调用Join以确保父任务已完成。

void TaskEntryPoint(Thread parent, ImportTask task)
{
  if (parent != null)
  {
    parent.Join(); // Wait for the parent task to complete.
  }
  task.Execute(); // Execute the child task.
}

现在您需要做的就是让父任务在各自独立的线程上运行,然后让子任务在各自独立的线程上运行。当调用TaskEntryPoint传递null时,如果是父任务,则为每个子任务传递相应的Thread实例。

<强>更新

根据您的评论,这里是我如何使用ThreadPool解决问题的示例。这是使用ThreadPool.RegisterWaitForSingleObject方法的相当高级的模式。它也恰好是一个极其可扩展的解决方案,因为它使用绝对最少的资源来等待WaitHandle发出信号。

foreach (ImportTask parent in parentTasks)
{
    // This WaitHandle will be used to get the child tasks going.
    var wh = new ManualResetEvent(false);

    // Needed to capture the loop variable correctly.
    var p = parent; 

    // Queue the parent task.
    ThreadPool.QueueUserWorkItem(
        (state) =>
        {
            try
            {
                // Execute the parent task.
                p.Execute();
            }
            finally
            {
                // Signal the event so that the child tasks can begin executing.
                wh.Set();
            }
        }, null);

    // Register the child tasks.
    foreach (ImportTask child in parent.ChildTasks)
    {
        // Needed to capture the loop variable correctly.
        var c = child;

        // Registers a callback for the child task that will execute once the
        // parent task is complete.
        RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(wh,
            (state, to) =>
            {
                // Execute the child task.
                c.Execute();
            }, 
            null, Timeout.Infinite, true);
    }
}

这里的神奇之处在于RegisterWaitForSingleObject等待事件的方式。它将注册一个回调,一旦发出事件信号将自动执行。但是,它以这样的方式执行它,即在等待该事件时池中没有任何线程被浪费。同样,这是一个相当复杂的模式,需要您的一些思考,但它将非常可扩展。


1 为每个任务启动新线程可能不是最佳策略。您是否考虑过使用ThreadPoolTask类?