我写了一个小工具来读取大文本文件并搜索包含搜索词的行。我将此作为学习TPL数据流的机会。
代码工作正常,除非搜索词靠近文件的最后。在这种情况下,uiResult
动作块不会被称为,除非中有断点。
我的理解是,数据在<{1}}之后从uiResult
发布到searcher
,后searcher
已完成(已处理完最后一个数据块)。由于数据已发布到uiResult
,因此在处理完数据后才能完成。
问题
为什么即使向其发布数据,uiResult
也会变得完整(除非在uiResult
中设置了断点)?
代码
这里是相关代码,尽可能减少:
ActionBlock<LineInfo> uiResult = new ActionBlock<LineInfo>(li =>
{
// If match found near end of file, the following line only runs
// if a breakpoint is set on it:
if (results != null) results.Add(li);
},
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 1,
CancellationToken = cancelSource.Token,
TaskScheduler = TaskScheduler.FromCurrentSynchronizationContext()
});
BatchBlock<LineInfo> batcher = new BatchBlock<LineInfo>(5000);
ActionBlock<LineInfo[]> searcher = new ActionBlock<LineInfo[]>(lines =>
{
foreach (LineInfo li in lines)
{
if (li.TextOfLine.Contains(searchTerm))
{
uiResult.Post(li);
}
}
},
new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 1,
CancellationToken = cancelSource.Token
});
batcher.LinkTo(searcher);
batcher.Completion.ContinueWith(t =>
{
if (t.IsFaulted) ((IDataflowBlock)searcher).Fault(t.Exception);
else searcher.Complete();
if (t.IsFaulted) ((IDataflowBlock)uiResult).Fault(t.Exception);
else uiResult.Complete();
});
Task.Run(() =>
{
using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
string line;
while ((line = sr.ReadLine()) != null && cancelSource.IsCancellationRequested == false)
{
batcher.Post(new LineInfo() { LineNumber = lineNumber, OffsetOfLine = offset, TextOfLine = line });
}
batcher.Complete();
try
{
searcher.Completion.Wait();
uiResult.Completion.Wait();
}
catch (AggregateException ae)
{
TaskCanceledException taskCancelled = ae.InnerException as TaskCanceledException;
if (taskCancelled != null)
{
// Swallow the Exception if is just a user cancellation
throw;
}
}
finally
{
signalDone();
}
}
});
答案 0 :(得分:1)
由于您处理完成的方式,您的代码是不确定的。可能的事件顺序如下:
Task
处理整个文件,并在Complete()
上致电batcher
。batcher
处理最后一批,将其发送到searcher
并完成。Complete()
和searcher
上调用uiResult
。uiResult
没有工作要做,所以完成了。searcher
处理最后一批,尝试将每个结果发送到uiResult
。但uiResult
已经完成,所以拒绝一切。这意味着Post()
会返回false
,但您不会检查它。所以问题是你正在尝试向已经完成的块发送一些东西,但这不起作用。
解决方案是仅在块完成之前调用块Complete()
(即其Completion
完成)。可能最简单的方法是将PropagateCompletion
与LinkTo()
一起使用。