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

C# 如何使用负载平衡和有限并行度的任务并行库(TPL)?,c#,.net,concurrency,task-parallel-library,async-await,C#,.net,Concurrency,Task Parallel Library,Async Await,我的任务是使用(异步)接口将已知的nr值写入外部系统。我必须限制并发执行的最大并行写入数。此外,我还必须使用负载平衡,因为外部系统编写某些值可能需要更长的时间 我知道如何单独解决这些问题: 并行度: new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites} var partitioner = Partitioner.Create(values.ToList(), true); var writeTask = Task

我的任务是使用(异步)接口将已知的nr值写入外部系统。我必须限制并发执行的最大并行写入数。此外,我还必须使用负载平衡,因为外部系统编写某些值可能需要更长的时间

我知道如何单独解决这些问题:

并行度:

new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites}
var partitioner = Partitioner.Create(values.ToList(), true);
var writeTask = Task<AccessResult>.Factory.FromAsync(BeginWriteValue, EndWriteValue, value.SystemId, value.Xml, priority, null);
我还偶然发现了这篇文章:

负载平衡:

new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites}
var partitioner = Partitioner.Create(values.ToList(), true);
var writeTask = Task<AccessResult>.Factory.FromAsync(BeginWriteValue, EndWriteValue, value.SystemId, value.Xml, priority, null);
来自异步接口的任务:

new ParallelOptions {MaxDegreeOfParallelism = maxNrParallelWrites}
var partitioner = Partitioner.Create(values.ToList(), true);
var writeTask = Task<AccessResult>.Factory.FromAsync(BeginWriteValue, EndWriteValue, value.SystemId, value.Xml, priority, null);
我特别不确定前面代码的最后一部分:执行工作负载的操作。与其直接创建WriteValueTask,不如像这样使用同步接口:

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

或者可以创建一个任务,然后直接等待它(task.WaitAny(…)?

您应该使用TPL Dataflow的
ActionBlock
,它为您封装了所有这些。这是一个基于参与者的框架,是TPL的一部分:

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

foreach (var value in values)
{
    block.Post(value);
}
var block=新动作块(
value=>GetWriteValueTask(值,优先级)
新的ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism=GetMaxNrParallelWrites();
});
foreach(值中的var值)
{
block.Post(值);
}
您可以设置
MaxDegreeOfParallelism
BoundedCapacity
并加入负载平衡,因为它一次只处理
MaxDegreeOfParallelism
项,当每个项完成时,它会处理下一项(而不是使用预先对集合进行分区的
Partitioner


注意:当您接受一个
异步
任务并等待它同步完成(即
任务.WaitAny
)时,实际上没有什么是异步的。在这种情况下,您应该使用
Task.whany

中有一个很好的示例,介绍了如何在中创建负载平衡
ForEachASync
方法。我已取出
任务。运行
以避免启动新线程,然后扩展方法变为:

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

当涉及到并行异步工作时,负载平衡是很棘手的,所以在处理完设计的其余部分之前,最好先把它放在一边(l3arnon的答案肯定是朝着正确的方向迈出的一步),并且只有在您最终确定长时间运行的调用(异常值)时,才进行负载平衡如果一堆长时间运行的任务随机地进入同一个分区,那么分区是否存在效率低下的问题?因为所有长时间运行的任务都必须按顺序处理,而快速任务早就完成了吗?@samwise 100个任务是并行处理的,一旦快速任务完成,只要总数小于或等于100,就可以启动更多的任务。@NedStoyanov现在我明白了。每个任务都是一个创建的分区。这是有效的还是仅仅是为了达到目标而滥用分区?但无论如何,谢谢你们,我以前从未使用过TPL,也学到了你们很多东西。谢谢。我已经测试过了,我认为它非常适合我的需要。我对ActionBlock类的工作方式非常感兴趣。是否有一个要处理的带有已发布值的集合,这些值在处理时会从此缓冲区中删除?我没有找到详细描述这一点的MSDN文章。@samwise在ActionBlock中有一个内部队列。如果您没有任何访问权限,可以使用
InputCount
属性获取其大小。以下是有关数据流的更多信息: