在c# .Net 3.5中,如何控制多个WaitHandles?
我有以下情况:
我的应用程序必须从另一个应用程序导入多组数据,时间至关重要。
因此,它会为每次导入生成一个线程。
因此,假设我有导入 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您知道
如果线程本质上是简单线性的,并且您不这样做不需要排队来实现可扩展性,您也许可以使用 C#:等待所有待完成的线程
Did you know
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
如果您确实想为每个任务分配一个线程,则根本不需要使用
WaitHandle
实例。1 您可以传递Thread
正在执行父任务的实例,连接到每个子任务并调用Join
以确保父任务已完成。现在您需要做的就是让父任务在它们自己的单独线程上运行,然后让子任务在它们自己的单独线程上运行。调用
TaskEntryPoint
时,如果是针对父任务,则传递null
,并为每个子任务传递适当的Thread
实例。更新:
根据您的评论,这里是我如何使用
ThreadPool
解决问题的示例。这是使用 ThreadPool.RegisterWaitForSingleObject 方法的相当高级的模式。它也恰好是一个高度可扩展的解决方案,因为它使用绝对最少的资源来等待WaitHandle
收到信号。这里的神奇之处在于
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 theThread
instance, which is executing the parent task, to each child task and callJoin
to make sure the parent task has finished.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
passnull
if it is for a parent task and pass the appropriateThread
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 theThreadPool.RegisterWaitForSingleObject
method. It also happens to be an extremely scalable solution since it uses the absolute minimum of resources to wait for aWaitHandle
to get signaled.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
orTask
classes?