【发布时间】:2020-12-18 14:08:53
【问题描述】:
我有一个数据流管道,它由多个处理异构文档(XLS、PDF 等)的块组成。每种类型的文档都由专用的TransformBlock 处理。在管道的末端,我有一个ActionBlock,它接收所有已处理的文档,并将它们一一上传到 Web 服务器。我的问题是我找不到一种方法来满足按照最初输入管道的相同顺序上传文档的要求。例如,我不能使用 EnsureOrdered 选项来发挥我的优势,因为此选项配置单个块的行为,而不是并行工作的多个块的行为。我的要求是:
- 按特定顺序将文档插入管道中。
- 根据文档类型以不同方式处理每个文档。
- 应按顺序处理特定类型的文档。
- 可以(并且应该)并行处理不同类型的文档。
- 所有文件都应在处理完毕后尽快上传。
- 文档必须按顺序上传,并按照它们在管道中输入的顺序。
例如要求文档#8必须在文档#7之后上传,即使它是在文档#7之前处理的。
第五个要求的意思是我等不及所有文档都处理完,然后按索引排序,最后上传。上传必须与处理同时进行。
这是我正在尝试做的一个最小示例。为简单起见,我没有使用IDocument 接口的实例来提供块,而是使用简单的整数。每个整数的值代表它进入管道的顺序,以及必须上传的顺序:
var xlsBlock = new TransformBlock<int, int>(document =>
{
int duration = 300 + document % 3 * 300;
Thread.Sleep(duration); // Simulate CPU-bound work
return document;
});
var pdfBlock = new TransformBlock<int, int>(document =>
{
int duration = 100 + document % 5 * 200;
Thread.Sleep(duration); // Simulate CPU-bound work
return document;
});
var uploader = new ActionBlock<int>(async document =>
{
Console.WriteLine($"Uploading document #{document}");
await Task.Delay(500); // Simulate I/O-bound work
});
xlsBlock.LinkTo(uploader);
pdfBlock.LinkTo(uploader);
foreach (var document in Enumerable.Range(1, 10))
{
if (document % 2 == 0)
xlsBlock.Post(document);
else
pdfBlock.Post(document);
}
xlsBlock.Complete();
pdfBlock.Complete();
_ = Task.WhenAll(xlsBlock.Completion, pdfBlock.Completion)
.ContinueWith(_ => uploader.Complete());
await uploader.Completion;
输出是:
Uploading document #1
Uploading document #2
Uploading document #3
Uploading document #5
Uploading document #4
Uploading document #7
Uploading document #6
Uploading document #9
Uploading document #8
Uploading document #10
理想的顺序是#1、#2、#3、#4、#5、#6、#7、#8、#9、#10。
在将已处理文档发送到uploader 块之前,如何恢复已处理文档的顺序?
澄清:通过将多个特定的TransformBlocks 替换为单个通用的TransformBlock 来彻底改变管道的架构不是一种选择。理想的情况是拦截处理器和上传者之间的单个块,这将恢复文档的顺序。
【问题讨论】:
-
正常的方法是在传输的开头添加一个序列号,以便块可以按正确的顺序重新组装。
-
@jdweng 您可以假设序列号已经是
document对象的属性。我可以向它们添加public long SequenceNumber属性,并正确初始化它。问题是如何在它们被所有这些不同的块处理后重新组装它们。 -
上传者需要在每个块前添加一个序列号,以便服务器收到块时,服务器可以按正确的顺序组合。您不能使用在处理过程中被删除的号码。
-
@jdweng 我不能将订单恢复委托给网络服务器(如果你是这个意思)。我必须在我自己的程序中这样做。
-
那你必须在上传前做,不能并行上传。
标签: c# task-parallel-library tpl-dataflow