Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 并行运行任务并连接输出_C#_.net_.net Core_Task Parallel Library - Fatal编程技术网

C# 并行运行任务并连接输出

C# 并行运行任务并连接输出,c#,.net,.net-core,task-parallel-library,C#,.net,.net Core,Task Parallel Library,我想获取多个数据提供者,它们返回相同的数据结构,但数据输出不同。最后,需要追加数据源的输出,以便我可以使用全部结果。为了提高性能,需要并行调用这些数据源。我现在有了这个解决方案: Task<List<Result>> dataSource1 = null; Task<List<Result>> dataSource2 = null; foreach (var dataSource in dataSourcesToBeFetched)

我想获取多个数据提供者,它们返回相同的数据结构,但数据输出不同。最后,需要追加数据源的输出,以便我可以使用全部结果。为了提高性能,需要并行调用这些数据源。我现在有了这个解决方案:

Task<List<Result>> dataSource1 = null;
Task<List<Result>> dataSource2 = null;
foreach (var dataSource in dataSourcesToBeFetched)
        {
            switch (dataSource)
            {
                case DataSource.DataSource1:
                    dataSource1 = DataSource1();
                    break;

                case DataSource.DataSource2:
                    dataSource2 =DataSource2();
                    break;
            }
        }
await Task.WhenAll(dataSource1, dataSource2);
var allData = dataSource1.Result.Append(dataSource2.Result)
任务数据源1=null;
任务数据源2=null;
foreach(dataSourcesToBeFetched中的var数据源)
{
交换机(数据源)
{
案例数据源.DataSource1:
dataSource1=dataSource1();
打破
案例数据源.DataSource2:
dataSource2=dataSource2();
打破
}
}
等待任务。WhenAll(数据源1、数据源2);
var allData=dataSource1.Result.Append(dataSource2.Result)

但我对此并不满意。添加更多数据源时,我需要将新结果附加到列表中,这看起来很难看。除此之外,我想使用switch表达式,但我正在努力解决这个问题。

所有这些代码都可以替换为:

var results=await Task.WhenAll(DataSource1(),DataSource2());
该方法返回一个带有所有异步操作结果的
任务

获得结果后,您可以将其与可枚举的
合并。选择many

var flattened=results.SelectMany(r=>r).ToList();
虽然可以将这两种操作结合起来,但最好避免。这导致代码难以读取、维护和调试。在调试期间,您通常希望在
等待
后中断,以检查结果是否为空或其他意外值

任务和扁平化在不同的线程上运行,这使得使用链接调用进行调试变得更加困难

如果确实需要,可以在
whalll
之后使用
ContinueWith
在返回结果之前在线程池线程中处理结果:

var flatten=await Task.WhenAll(DataSource1(),DataSource2())
                      .ContinueWith(t=>t.Results.SelectMany(r=>r)
                                        .ToList());
更新

要过滤源,一种快速而肮脏的方法是创建一个
字典
,将源ID映射到方法,并使用LINQ的
选择
来选择它们:

//In a field
Dictionary<DataSource,Func<Task<List<Result>>>> map=new (){
    [DataSource.Source1]=DataSource1,
    [DataSource.Source1]=DataSource2
};

//In the method
DataSource[] fetchSources=new DataSource[0];
var tasks=fetchSources.Select(s=>map[s]());
//在字段中
字典映射=新建(){
[DataSource.Source1]=数据源1,
[DataSource.Source1]=数据源2
};
//在方法中
DataSource[]fetchSources=新数据源[0];
var tasks=fetchSources.Select(s=>map[s]());
但这与使用函数执行相同的任务没有什么不同:

DataSource[] fetchSources=new DataSource[0];
var tasks=fetchSources.Select(s=>RunSource(s));
//or even 
//var tasks=fetchSources.Select(RunSource);
    
var results=await Task.WhenAll(tasks);
var flattened=results.SelectMany(r=>r).ToList();


public static Task<List<Result>> RunSource(DataSource source)
{
    return source switch {
            DataSource.Source1=> DataSource1(),
            DataSource.Source2=> DataSource2(),
            _=>throw new ArgumentOutOfRangeException(nameof(source))
    };
}
DataSource[]fetchSources=新数据源[0];
var tasks=fetchSources.Select(s=>RunSource);
//甚至
//var tasks=fetchSources.Select(RunSource);
var结果=等待任务.WhenAll(任务);
var=results.SelectMany(r=>r.ToList();
公共静态任务运行源(数据源)
{
返回源开关{
DataSource.Source1=>DataSource1(),
DataSource.Source2=>DataSource2(),
_=>抛出新ArgumentOutOfRangeException(名称(源))
};
}

代码中的一个问题是,如果要删除的数据源中不存在
数据源.DataSource1
,则您正在等待一个空任务

我可能会选择一系列有待完成的任务

比如:

var dataSources = new List<Task<List<Result>>>();

// check if the DataSource1 is present in the dataSourcesToBeFetched
if(dataSourcesToBeFetched.Any(i => i == DataSource.DataSource1))
    dataSources.Add(DataSource1());

// check if the DataSource2 is present in the dataSourcesToBeFetched
if(dataSourcesToBeFetched.Any(i => i == DataSource.DataSource2))
    dataSources.Add(DataSource2());

// a list to hold all results
var allData = new List<Result>();

// if we need to fetch any, await all tasks.
if(dataSources.Count > 0)
{
    await Task.WhenAll(dataSources);

    // add the results to the list.
    foreach(var dataSource in dataSources)
        allData.AddRange(dataSource.Result);
}
var dataSources=newlist();
//检查要删除的数据源中是否存在数据源1
if(dataSourcesToBeFetched.Any(i=>i==DataSource.DataSource1))
Add(DataSource1());
//检查数据源2是否存在于要删除的数据源中
if(dataSourcesToBeFetched.Any(i=>i==DataSource.DataSource2))
Add(DataSource2());
//保存所有结果的列表
var allData=新列表();
//如果我们需要取,等待所有任务。
如果(dataSources.Count>0)
{
等待任务WhenAll(数据源);
//将结果添加到列表中。
foreach(数据源中的var数据源)
allData.AddRange(dataSource.Result);
}

任务。当所有的
都将返回所有结果。@Rogier为什么使用
任务。运行
进行I/O操作?@PeterCsala问得好。确实不需要。让我更新一下示例。一个简单的
var results=wait Task.whalll(DataSource1(),DataSource2())
就足够了。由于所有异步操作都会产生相同的输出,
结果
将是一个数组,其中包含所有异步操作的输出。您不希望等待
null
,如果要消除的数据源包含重复项,则可以覆盖
数据源1
(和另一个)以展平结果,您可以使用
(等待任务。当所有(…).SelectMany(x=>x).ToList()
@JL0PD更好地使用
结果。SelectMany()
相反。在这种情况下,链接方法会导致难以读取和维护的代码。异步操作不仅仅是要链接的调用,还有不同类型的错误。在调试过程中,在
wait
调用时或之后中断的可能性要比在合并后通过
dataSourcesToBeFe循环要大得多tched
因为并非所有的数据源都需要获取。有时只有一个,或者所有的数据源。您将如何实现这一部分?@Rogier问题代码的编写方式,看起来您使用了
开关
只分配给变量。您的实际问题是什么?不管是什么,可能有很多更简单的方法。如果有任何问题,您可以使用LINQ和
Where
来过滤项目,
选择
返回
任务
对象并等待它们。或者,如果您有ETL问题,您可以使用数据流类和
LinkTo
TransformManyBlock
路由/合并消息,分组块etc@Rogier我添加了两个示例选择要使用的源代码-一个带有字典,一个带有选择器函数。在您的案例中,您应该使用任何更容易使用的源代码