Parallel.Invoke等待任务繁忙

时间:2015-02-03 14:43:02

标签: c# .net asynchronous parallel-processing task-parallel-library

有人可以帮我提供以下代码吗?在行:

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个忙消息,则等待其中一个可用。

1 个答案:

答案 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解释为最大并发度。