【问题标题】:In c# .Net 3.5, how can I control multiple WaitHandles?在 c# .Net 3.5 中,如何控制多个 WaitHandles?
【发布时间】:2011-08-28 23:33:52
【问题描述】:

我有以下情况:

我的应用程序必须从另一个应用程序导入多组数据,时间很关键。
因此,它为每次导入生成一个线程。
所以,假设我有 1 到 10 的进口,其中进口 4 和 5 只能在进口 2 之后的进口 1、6 和 7 以及进口 3 之后的 8、9 和 10 之后运行

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

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

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

更新:
在 Brian Gideon 的回答之后,我想到了:
dateSince = Convert.ToDateTime(txtBoxDateSince.Text); dateTo = Convert.ToDateTime(txtBoxDateTo.Text);

//在时间间隔上循环所有天 而 (DateTime.Compare(dateSince, dateTo)

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);

}//结束 while (DateTime.Compare(since,to)
几乎相同的答案,只是使用了没有 lambda 表达式的方法并在它们上使用了参数。
我仍然没有对它进行压力测试,但它工作得很好。

谢谢布赖恩。

【问题讨论】:

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


    【解决方案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 类?

    【讨论】:

    • 对不起,布莱恩。我没有具体说明,但我使用 ThreadPool 来管理线程,因为在一年左右的时间里,我可能会为这个进程创建 200 多个线程。我还能在 ThreadPool 中使用你的逻辑吗?
    • 非常感谢您的回答。我会尝试实现它并让你知道它是如何结束的。就一个问题。我对 lambda 表达式不是很熟悉,所以 (state, to) => { // 执行子任务上的“to”是什么。 c.执行(); }, null, Timeout.Infinite, true);
    • @Huggy:这是WaitOrTimerCallback 所需的参数之一。在我的示例中未使用它。当然,您不必使用 lambda 表达式。如果您愿意,可以单独定义方法。
    【解决方案2】:

    你知道吗

    1. WaitHandle.WaitAny Method
    2. WaitHandle.WaitAll Method

    如果线程本质上是简单的线性,并且您不需要排队以实现可伸缩性,您也许可以使用C#: Waiting for all threads to complete

    【讨论】:

    • 谢赫,只有一些是线性的,我确实需要排队以实现可扩展性。我检查了这两种方法,但问题是检查一些线程,以启动其他一组线程,而不是全部。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 2011-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多