有人可以帮我提供以下代码吗?在行:
Parallel.Invoke(parallelOptions, () => dosomething(message));
我想调用最多5个并行任务(如果有5个忙,等待下一个可用,然后启动它......如果只有4个忙,启动5个等)
private AutoResetEvent autoResetEvent1 = new AutoResetEvent(false);
private ParallelOptions parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = 5 };
private void threadProc()
{
queue.ReceiveCompleted += MyReceiveCompleted1;
Debug.WriteLine("about to start loop");
while (!shutdown)
{
queue.BeginReceive();
autoResetEvent1.WaitOne();
}
queue.ReceiveCompleted -= MyReceiveCompleted1;
queue.Dispose();
Debug.WriteLine("we are done");
}
private void MyReceiveCompleted1(object sender, ReceiveCompletedEventArgs e)
{
var message = queue.EndReceive(e.AsyncResult);
Debug.WriteLine("number of max tasks: " + parallelOptions.MaxDegreeOfParallelism);
Parallel.Invoke(parallelOptions, () => dosomething(message));
autoResetEvent1.Set();
}
private void dosomething(Message message)
{
//dummy body
var i = 0;
while (true)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
i++;
if (i == 5 || i == 10 || i == 15) Debug.WriteLine("loop number: " + i + " on thread: " + Thread.CurrentThread.ManagedThreadId);
if (i == 15)
break;
}
Debug.WriteLine("finished task");
}
我现在得到的结果:
1)如上所述使用dosomething(),我一次只能获得一个(等待)
2)dosomething()更改为以下,我得到任何停止x任务数量(不限制或服从MaxDegreeOfParallelism
private async Task dosomething(Message message)
{
//dummy body
var i = 0;
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(1));
i++;
if (i == 5 || i == 10 || i == 15) Debug.WriteLine("loop number: " + i + " on thread: " + Thread.CurrentThread.ManagedThreadId);
if (i == 15)
break;
}
Debug.WriteLine("finished task");
}
为了得到我想要的成就,我做错了什么?
我想要的结果:
在“MyReceiveCompleted”中,我想确保只有5个同步任务正在处理消息,如果有5个忙消息,则等待其中一个可用。
答案 0 :(得分:1)
这行代码:
Parallel.Invoke(parallelOptions, () => dosomething(message));
告诉TPL用只做一件事开始新的并行操作。所以," max parallelism" 5的选择是没有意义的,因为只有一件事要做。 autoResetEvent1
确保一次只有一个并行操作,并且每个并行操作只有一件事要做,因此完全可以预期一次只运行一件事的行为。
当您将委托更改为异步时,实际发生的是MaxDegreeOfParallelism
仅应用于方法的同步部分。因此,一旦它遇到第一个await
,它就会离开"离开"并行操作不再受其限制。
核心问题是Parallel
在提前知道操作次数时效果最佳 - 您的代码并不知道;它只是从队列中读取它们并在它们到达时对它们进行处理。正如有人评论的那样,可以使用动态任务并行来解决这个问题,TaskScheduler
会限制并发性。
但是,最简单的解决方案可能是TPL Dataflow。您可以使用适当的限制选项创建ActionBlock<T>
,并在到达时向其发送消息:
private ActionBlock<string> block = new ActionBlock<string>(
message => dosomething(message),
new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 5 });
private void MyReceiveCompleted1(object sender, ReceiveCompletedEventArgs e)
{
var message = queue.EndReceive(e.AsyncResult);
block.Post(message);
autoResetEvent1.Set();
}
TPL Dataflow的一个不错的方面是它也理解异步方法,并将MaxDegreeOfParallelism
解释为最大并发度。