C# TPL数据流-最终确定块

C# TPL数据流-最终确定块,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,我有一个网络刮工作管道。它根据URL下载页面,解析内容,然后将结果分成块(每个x元素都保存到DB中),工作正常 但我还需要一个额外的步骤,它将总结在管道中所做的一切。我当前的实现是通过管道传递相同的对象(在接下来的步骤中只添加一些值),因此我制作了一些复制代码,这应该显示我想要实现的目标 Console.WriteLine($"Processing started: {DateTime.Now.ToString()}"); var workBuffer = new Batch

我有一个网络刮工作管道。它根据URL下载页面,解析内容,然后将结果分成块(每个x元素都保存到DB中),工作正常

但我还需要一个额外的步骤,它将总结在管道中所做的一切。我当前的实现是通过管道传递相同的对象(在接下来的步骤中只添加一些值),因此我制作了一些复制代码,这应该显示我想要实现的目标

Console.WriteLine($"Processing started: {DateTime.Now.ToString()}");
var workBuffer = new BatchBlock<string>(3);
var resultsToFinalize = new List<string>();
var downloadUrl = new TransformBlock<string, string>(url =>
{
    Thread.Sleep(int.Parse(url.Last().ToString()) * 1000);
    return url;
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

var parseContent = new TransformBlock<string, string>(content =>
{
    Thread.Sleep(int.Parse(content.Last().ToString()) * 1000 / 2);
    return $"parsing result for: {content}";
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

var saveToDb = new ActionBlock<string[]>(results =>
{
    Console.WriteLine($"results: {DateTime.Now.ToString()} {String.Join(", ", results)}");
    results.ToList().ForEach(t => resultsToFinalize.Add(t));
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 1 });

downloadUrl.LinkTo(parseContent, new DataflowLinkOptions
{
    PropagateCompletion = true
});
parseContent.LinkTo(workBuffer, new DataflowLinkOptions
{
    PropagateCompletion = true
});
workBuffer.LinkTo(saveToDb, new DataflowLinkOptions
{
    PropagateCompletion = true
});

Enumerable.Range(2, 9).Select(t => downloadUrl.Post($"http://some_site_to_parse.com{t}"));

downloadUrl.Complete();
saveToDb.Completion.Wait();
Console.WriteLine(String.Join(Environment.NewLine, resultsToFinalize));
Console.WriteLine($”处理已开始:{DateTime.Now.ToString()});
var workBuffer=新批处理块(3);
var resultsToFinalize=新列表();
var downloadUrl=新转换块(url=>
{
Sleep(int.Parse(url.Last().ToString())*1000);
返回url;
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=DataflowBlockOptions.Unbounded});
var parseContent=newtransformblock(内容=>
{
Sleep(int.Parse(content.Last().ToString())*1000/2);
返回$“对:{content}的分析结果”;
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=DataflowBlockOptions.Unbounded});
var saveToDb=新操作块(结果=>
{
WriteLine($”结果:{DateTime.Now.ToString()}{String.Join(“,”,results)});
results.ToList().ForEach(t=>resultsToFinalize.Add(t));
},新的ExecutionDataflowBlockOptions{MaxDegreeOfParallelism=1});
downloadUrl.LinkTo(解析内容,新数据流链接选项
{
完成=真
});
LinkTo(工作缓冲区,新的DataflowLinkOptions
{
完成=真
});
workBuffer.LinkTo(saveToDb,新的DataflowLinkOptions
{
完成=真
});
范围(2,9)。选择(t=>downloadUrl.Post($)http://some_site_to_parse.com{t} ”);
downloadUrl.Complete();
saveToDb.Completion.Wait();
Console.WriteLine(String.Join(Environment.NewLine,resultsToFinalize));
目前,它正在工作,因为有外部
resultsToFinalize
变量来收集所有结果

也许我应该将saveToDb改为TransformBlock。但是如何累积结果,直到整个管道准备就绪(最好是在所有事情(前一个区块)完成后再启动这个额外区块)

理论上,我可以在这个附加步骤之前再创建一个BatchBlock,并将其大小设置为Input size,但这似乎有点老套:)

否则你怎么解决这个问题

[更新12.08.2020 16:47] 有些人似乎无法理解我想要实现的目标。所以我要说清楚:我发布了一些代码。我希望有相同的输出,我的代码正在生成,因此:

处理已开始:12.08.2020 16:27:46

结果:12.08.2020 16:27:52解析结果:http://some_site_to_parse.com2,分析结果为:http://some_site_to_parse.com3,分析结果为:http://some_site_to_parse.com4

结果:12.08.2020 16:27:57解析结果:http://some_site_to_parse.com5,分析结果为:http://some_site_to_parse.com6,分析结果为:http://some_site_to_parse.com7

结果:12.08.2020 16:28:00解析结果:http://some_site_to_parse.com8,分析结果为:http://some_site_to_parse.com9,分析结果为:http://some_site_to_parse.com10

对以下项的分析结果:http://some_site_to_parse.com2

对以下项的分析结果:http://some_site_to_parse.com3

对以下项的分析结果:http://some_site_to_parse.com4

对以下项的分析结果:http://some_site_to_parse.com5

对以下项的分析结果:http://some_site_to_parse.com6

对以下项的分析结果:http://some_site_to_parse.com7

对以下项的分析结果:http://some_site_to_parse.com8

对以下项的分析结果:http://some_site_to_parse.com9

对以下项的分析结果:http://some_site_to_parse.com10

但不使用resultsToFinalize(使用TPL:)的功能)


我假设saveToDb应该从ActionBlock更改为TransformBlock。最后可能会有一些新的动作块。问题是如何设置它-这样它只会发射一次。

它不是黑客。如果你想批量写出数据,你需要一些东西来批量数据。一旦有了BatchBlock,就可以使用
SqlBulkCopy
或数据库提供的任何工具,以最少的日志记录批量导入数据,而不是逐个插入行。否,数据库部分正在工作。每个示例中都有5页。这不是问题所在。我只需要再做一步,收集所有页面(因此saveToDb可能应该从ActionBlock更改为TransfromBlock,然后再添加一个块(可能是ActionBlock)但是,按照我刚才描述的那样做意味着——最后一个ActionBlock将被激发的次数与@Panagiotis Kanavos-SaveToDb transformBlock一样多(我希望它在收集所有结果后只激发一次)当然,它可以在没有外部列表的情况下通过使用TPL数据块来完成。您到底需要什么?发布实际演示problem@PanagiotisKanavos-这有关系吗?:)这应该完全无关:)@Panagiotis Kanavos-我已经发布了代码。提案的结果应该和现在一样,但是没有
resultsToFinalize
variable,这不是很粗糙。如果你想批量写出数据,你需要一些东西来批量数据。一旦有了BatchBlock,就可以使用
SqlBulkCopy
或数据库提供的任何工具,以最少的日志记录批量导入数据,而不是逐个插入行。否,数据库部分正在工作。每个示例中都有5页。这不是问题所在。我只需要再做一步,收集所有页面(因此saveToDb可能应该从ActionBlock更改为TransfromBlock,并再添加一个块(可能是ActionBlock),但按照我刚才所描述的那样做意味着最后一个ActionBlock将被激发同样多的时间