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

发布于 2024-11-08 23:19:56 字数 2525 浏览 0 评论 0原文

我有以下情况:

我的应用程序必须从另一个应用程序导入多组数据,时间至关重要。
因此,它会为每次导入生成一个线程。
因此,假设我有导入 1 到 10,其中导入 4 和 5 只能在导入 1、6 和 7 后运行,在导入 2 后运行,在导入 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);

//循环时间间隔上的所有日期 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);

}//结束 while (DateTime.Compare(since,to) < 0)

几乎相同的答案,只是使用了没有 lambda 表达式的方法并在其上使用了参数。
我还没有对其进行压力测试,但它运行得很好。

谢谢布莱恩。

I have the following scenario:

My application must import several sets of data from another application, and time is critical.
Because of that, it spawns one thread for each import.
So, say I have imports 1 througth 10, where imports 4 and 5 can run only after import 1, 6 and 7 after import 2 and 8, 9 and 10 after import 3

  • Import 1
    • Import 4
    • Import 5
  • Import 2
    • Import 6
    • Import 7
  • Import 3
    • Import 8
    • Import 9
    • Import 10

Searching around stackoverflow i found a few answers about waithandles but I'm not sure to control multiple waithandles.
I thougth about creating a list of import/handles and checking on them on a loop but i don't know how to fire the .set() from there.
Or creating a thread for each father import and instantiate the handle inside it, and that father thread fires a thread for each child import, but handling threads of a thread is something i'm not sure is correct.

Any ideias on how can i solve this ?

UPDATE:
After Brian Gideon's answer heres what i came up with:
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);

}//End while (DateTime.Compare(since,to) < 0)

Pretty much the same answer, just used the methods without lambda expressions and used parameters on them.
I still didn't stress test it, but it's working pretty good.

Thanks Brian.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

花心好男孩 2024-11-15 23:19:56

您知道

  1. WaitHandle.WaitAny 方法
  2. < a href="http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx" rel="nofollow noreferrer">WaitHandle.WaitAll 方法

如果线程本质上是简单线性的,并且您不这样做不需要排队来实现可扩展性,您也许可以使用 C#:等待所有待完成的线程

Did you know

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

If the threads are simple linear in nature and you don't require queuing for scalability, you might be able to use C#: Waiting for all threads to complete

初与友歌 2024-11-15 23:19:56

如果您确实想为每个任务分配一个线程,则根本不需要使用 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为每个任务启动一个新线程可能不是最好的策略。您是否考虑过使用 ThreadPool 或 Task 类?

If you really want to dedicate one thread for each task then there is no need to use WaitHandle instances at all.1 You could pass the Thread instance, which is executing the parent task, to each child task and call Join to make sure the parent task has finished.

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

Now all you need to do is get the parent tasks going on their own separate threads and then get the child tasks going on their own separate threads. When calling TaskEntryPoint pass null if it is for a parent task and pass the appropriate Thread instance for each child task.

Update:

Based on your comment here is an example of how I might approach the problem using the ThreadPool. This is a fairly advanced pattern using the ThreadPool.RegisterWaitForSingleObject method. It also happens to be an extremely scalable solution since it uses the absolute minimum of resources to wait for a WaitHandle to get signaled.

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

The magic here is in the way RegisterWaitForSingleObject waits on the event. It will register a callback that will be automatically executed once the event is signaled. But, it does it in such a manner that no thread in the pool is wasted on waiting for that event. Again, this a fairly sophisticated pattern that will require some thought on your part, but it will be very scalable.


1Starting a new thread for each task might not be the best strategy. Have you considered using the ThreadPool or Task classes?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文