简体   繁体   English

如何使用负载均衡和有限并行度的任务并行库(TPL)?

[英]How to use task parallel library (TPL) with load balancing and limited degree of parallelism?

My task is to write a known nr of values to an external system by using an (async) interface. 我的任务是使用(异步)接口将已知的nr值写入外部系统。 I have to limit the maximum number of parallel writes that are executed concurrently. 我必须限制并发执行的最大并行写入次数。 Additionally I've got to use load balancing because it may take longer for some values to be written by that external system. 此外,我必须使用负载平衡,因为该外部系统可能需要更长的时间来写入某些值。

I know how to solve these problems each on it's own: 我知道如何解决这些问题本身:

Degree of parallelism: 并行度:

new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites}

I also stumbled over this article: http://msdn.microsoft.com/en-us/library/ee789351(v=vs.110).aspx 我也偶然发现了这篇文章: http//msdn.microsoft.com/en-us/library/ee789351(v = vs.110).aspx

Load balancing: 负载均衡:

var partitioner = Partitioner.Create(values.ToList(), true);

Task from async interface: 来自异步接口的任务:

var writeTask = Task<AccessResult>.Factory.FromAsync(BeginWriteValue, EndWriteValue, value.SystemId, value.Xml, priority, null);



But how do I correctly combine all this techniques? 但是,我如何正确地结合所有这些技术? I created the following code: 我创建了以下代码:

  int maxNrParallelWrites = GetMaxNrParallelWrites();
  var partitioner = Partitioner.Create(values.ToList(), true);
  Parallel.ForEach(partitioner, new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites},
    (val) =>
    {
      var writeValueTask = GetWriteValueTask(val, priority);
      Task.WaitAny(writeValueTask);
    });

I'm especially unsure about the the last part of the previous code: the action that executes the workload. 我特别不确定上一代码的最后一部分:执行工作负载的操作。 Would it be better instead of creating a WriteValueTask directly use the synchronous interface like this: 是否更好,而不是创建一个WriteValueTask直接使用这样的同步接口:

(val) =>
    {
      var accessResult = externalSystem.WriteValue(....);
    }

Or is it okay to create a task and then directly wait for it (Task.WaitAny(...))? 或者可以创建一个任务然后直接等待它(Task.WaitAny(...))?

You should use TPL Dataflow's ActionBlock that encapsulates all that for you. 您应该使用TPL Dataflow的ActionBlock来封装所有这些内容。 It's an actor based framework that is part of the TPL: 这是一个基于actor的框架,是TPL的一部分:

var block = new ActionBlock<Value>(
    value => GetWriteValueTask(value, priority)
    new ExecutionDataflowBlockOptions()
    {
        MaxDegreeOfParallelism = GetMaxNrParallelWrites();
    });

foreach (var value in values)
{
    block.Post(value);
}

You can set the MaxDegreeOfParallelism , BoundedCapacity and load balancing is baked in because it handles only MaxDegreeOfParallelism items at a time, and when each completes it handles the next one (as opposed to using a Partitioner that partitions the collection in advance) 您可以设置MaxDegreeOfParallelismBoundedCapacity和负载均衡,因为它一次只处理MaxDegreeOfParallelism项目,每次完成时它处理下一个项目(而不是使用预先分区集合的Partitioner程序)

Note: When you take an async task and wait for it to complete synchronously (ie Task.WaitAny ) nothing is actually asynchronous. 注意:当您执行async任务并等待它同步完成时(即Task.WaitAny )实际上没有任何异步。 You should be using Task.WhenAny instead in such cases. 在这种情况下,你应该使用Task.WhenAny

There is a good example of how to create a load balancing ForEachASync method in this article. 本文中有一个很好的示例,说明如何创建负载平衡ForEachASync方法 . I've taken out the Task.Run to avoid starting a new thread and then the extension method becomes this: 我已经取出Task.Run以避免启动新线程,然后扩展方法变为:

public static class Extensions
{
    public static async Task ExecuteInPartition<T>(IEnumerator<T> partition, Func<T, Task> body)
    {
        using (partition)
            while (partition.MoveNext())
                await body(partition.Current);
    }

    public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> body)
    {
        return Task.WhenAll(
            from partition in Partitioner.Create(source).GetPartitions(dop)
            select ExecuteInPartition(partition, body));
    }
}

Usage 用法

This example asynchronously processes a maximum of 100 emails at a time 此示例一次异步处理最多100封电子邮件

 // Process 100 emails at a time
 return emailsToProcess.ForEachAsync(100, ProcessSingleEmail);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM