任务无法运行

时间:2013-02-27 19:47:57

标签: .net task-parallel-library tpl-dataflow

我写了一个小工具来读取大文本文件并搜索包含搜索词的行。我将此作为学习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();
            }
        }
    });

1 个答案:

答案 0 :(得分:1)

由于您处理完成的方式,您的代码是不确定的。可能的事件顺序如下:

  1. Task处理整个文件,并在Complete()上致电batcher
  2. batcher处理最后一批,将其发送到searcher并完成。
  3. 继续执行,在Complete()searcher上调用uiResult
  4. 由于uiResult没有工作要做,所以完成了。
  5. searcher处理最后一批,尝试将每个结果发送到uiResult。但uiResult已经完成,所以拒绝一切。这意味着Post()会返回false,但您不会检查它。
  6. 所以问题是你正在尝试向已经完成的块发送一些东西,但这不起作用。

    解决方案是仅在块完成之前调用块Complete()(即其Completion完成)。可能最简单的方法是将PropagateCompletionLinkTo()一起使用。