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

Posted

技术标签:

【中文标题】在 c# .Net 3.5 中,如何控制多个 WaitHandles?【英文标题】:In c# .Net 3.5, how can I control multiple 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

搜索 *** 我发现了一些关于等待句柄的答案,但我不确定控制多个等待句柄。 我想创建一个导入/句柄列表并在循环中检查它们,但我不知道如何从那里触发 .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 表达式的方法并在它们上使用了参数。 我仍然没有对它进行压力测试,但它工作得很好。 谢谢布赖恩。

【问题讨论】:

【参考方案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】:

你知道吗

    WaitHandle.WaitAny Method WaitHandle.WaitAll Method

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

【讨论】:

谢赫,只有一些是线性的,我确实需要排队以实现可扩展性。我检查了这两种方法,但问题是检查一些线程,以启动其他一组线程,而不是全部。

以上是关于在 c# .Net 3.5 中,如何控制多个 WaitHandles?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 在 ASP.NET 3.5 中动态设置“application/ld+json”Schema.org 元数据

如何在 c# .net CF 3.5 中使用 XmlDocument 向 xml 添加属性

使用 .NET Framework 3.5 在 C# 中调用 Web API

c# .NEt 3.5 以用户形式运行进程时无法隐藏 CMD 窗口 - 窗体应用程序

将 C# .net 4 代码编译为 .net 3.5?

如何在对 C# .NET 3.5 服务器进行 DCOM 调用时可靠地检查客户端身份?