【发布时间】:2020-08-07 05:37:21
【问题描述】:
这个问题真的很难调试,并不总是发生(不会在短时间内发生,所以我可以轻松地调试代码)并且看起来没有人遇到过这样的类似问题? (我已经用谷歌搜索了几个小时,但没有找到与此问题相关的任何内容)。
简而言之,我的数据流网络在某些时候工作正常,直到我发现终端块(更新 UI)似乎停止工作(UI 上没有更新新数据),而所有向上的数据流块都是仍然工作正常。所以这里的其他块和ui块之间好像有些脱节。
这是我详细的数据流网络,让我们先检查一下,然后再详细解释这个问题:
//the network graph first
[raw data block]
-> [switching block] -> [data counting block]
-> [processing block] -> [ok result block] -> [completion monitoring]
-> [not ok result block] -> [completion monitoring]
//in the UI code behind where I can consume the network and plug-in some other blocks for updating
//like this:
[ok result block] -> [ok result counting block]
[not ok result block] -> [other ui updating]
块[ok result block] 是一个BroadcastBlock,它将结果推送到[ok result counting block]。我在这里部分描述的问题是 [ok result counting block] 似乎与 [ok result block] 断开连接。
var options = new DataflowBlockOptions { EnsureOrdered = false };
var execOptions = new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 80 };
//[raw data block]
var rawDataBlock = new BufferBlock<Input>(options);
//[switching block]
var switchingBlock = new TransformManyBlock<Input,Input>(e => new[] {e,null});
//[data counting block]
var dataCountingBlock = new BroadcastBlock<Input>(null);
//[processing block]
var processingBlock = new TransformBlock<Input,int>(async e => {
//call another api to compute the result
var result = await …;
//rollback the input for later processing (some kind of retry)
if(result < 0){
//per my logging, there is only one call dropping
//in this case
Task.Run(rollback);
}
//local function to rollback
async Task rollback(){
await rawDataBlock.SendAsync(e).ConfigureAwait(false);
}
return result;
}, execOptions);
//[ok result block]
var okResultBlock = new BroadcastBlock<int>(null, options);
//[not ok result block]
var notOkResultBlock = new BroadcastBlock<int>(null, options);
//[completion monitoring]
var completionMonitoringBlock = new ActionBlock<int>(e => {
if(rawDataBlock.Completion.IsCompleted && processingBlock.InputCount == 0){
processingBlock.Complete();
}
}, execOptions);
//connect the blocks to build the network
rawDataBlock.LinkTo(switchingBlock);
switchingBlock.LinkTo(processingBlock, e => e != null);
switchingBlock.LinkTo(dataCountingBlock, e => e == null);
processingBlock.LinkTo(okResultBlock, e => e >= 9);
processingBlock.LinkTo(notOkResultBlock, e => e < 9);
okResultBlock.LinkTo(completionMonitoringBlock);
notOkResultBlock.LinkTo(completionMonitoringBlock);
在后面的 UI 代码中,我插入了一些其他 UI 块来更新信息。这里我使用的是WPF,但我认为这里没关系:
var uiBlockOptions = new ExecutionDataflowBlockOptions {
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
};
dataCountingBlock.LinkTo(new ActionBlock<int>(e => {
//these are properties in the VM class, which is bound to the UI (xaml view)
RawInputCount++;
}, uiBlockOptions));
okResultBlock.LinkTo(new ActionBlock<int>(e => {
//these are properties in the VM class, which is bound to the UI (xaml view)
ProcessedCount++;
OkResultCount++;
}, uiBlockOptions));
notOkResultBlock.LinkTo(new ActionBlock<int>(e => {
//these are properties in the VM class, which is bound to the UI (xaml view)
ProcessedCount++;
PendingCount = processingBlock.InputCount;
}, uiBlockOptions));
我确实有代码监控块的完成状态:rawDataBlock、processingBlock、okResultBlock、notOkResultBlock。
我在processingBlock 中还有其他日志记录代码来帮助诊断。
正如我所说,经过相当长的一段时间(大约 1 小时处理了大约 60 万个项目,实际上这个数字没有说明问题,它可能是随机的),网络似乎仍然运行良好,除了一些计数( ok 结果,not ok 结果)没有更新,好像okResultBlock 和notOkResultBlock 与processingBlock 断开连接,或者它们与UI 块断开连接(更新UI)。我确保processingBlock 仍在工作(没有记录异常并且结果仍然写入文件),dataCountingBlock 仍然工作良好(在 UI 上更新了新计数),所有块 processingBlock,@ 987654343@、notOkResultBlock 未完成(它们的完成是 .ContinueWith 一个注销状态且未记录任何内容的任务)。
所以它真的卡在那里。我不知道为什么它会停止那样工作。这只有在我们使用像TPL Dataflow 这样的黑盒库时才会发生。我知道您可能也很难诊断、想象和思考可能性。我只是在这里向您询问解决此问题的建议以及您的任何共享经验(关于类似问题),并可能对TPL Dataflow中可能导致此类问题的一些猜测@
更新:
在我准备一些代码写下一些信息以帮助调试之前,我已经成功地再次重现了该错误。现在问题一直持续到这一点:processingBlock 不知何故实际上并没有将任何消息推送/发布/发送到所有链接块(包括okResultBlock 和notOkResultBlock)甚至是一个新块(前面加上@987654351 @ 有Append 的错误)链接到它无法接收任何消息(结果)。正如我所说,processBlock 似乎仍然可以正常工作(它的Action 确实在内部运行代码并正常生成结果日志记录)。所以这仍然是一个很奇怪的问题。
简而言之,现在的问题变成了为什么processBlock 无法将其消息发送/发布到所有其他链接块?是否有任何可能的原因导致这种情况发生?如何知道区块是否链接成功(调用.LinkTo之后)?
【问题讨论】:
-
评论不用于扩展讨论;这个对话是moved to chat。
-
您能否通过删除所有不相关的内容来更新您的问题,只留下重现问题所需的最少数据流块?
-
@TheodorZoulias 实际上代码没有改变。我只是添加更多代码(通过
Button的点击处理),该代码仅在我检测到(通过我的眼睛查看 UI)发生错误时运行。所以我在那个按钮点击上运行的代码只是为了写块当时的状态(广播块notOkResultBlock和okResultBlock保留他们收到的最后一个值,而不是任何新的发布给他们),我什至尝试链接processingBlock到一个新块(正如我在更新中所说的那样),但该新块不会注销任何内容。 -
任务直接从
WaitingForActivation转换到RanToCompletion并没有什么奇怪的。有two types of tasks、与Action关联的任务以及表示异步完成的任务。第一种通常称为“委托式任务”,第二种称为“承诺式任务”。数据流块的Completion属性是promise 样式任务的示例。只有委托任务在其生命周期内转换为Running状态。 -
是的,在 Microsoft 的产品中发现错误并非闻所未闻,但莫名其妙的行为很可能是程序员错误的结果。 :-)
标签: c# wpf task-parallel-library tpl-dataflow