在.NET 3.5上并行 - 线程无法在嵌套中完成任务

时间:2011-09-15 23:53:18

标签: .net-3.5 nested parallel-processing

我刚刚在DevLabs上下载了.NET 3.5 SP1的Parallel Extensions并开始使用它。一切似乎工作正常,直到我注意到日志中有一些例外,我很难理解它们为什么会发生。

以下是并行任务的片段:

Parallel.ForEach(myJobArray, currentJob =>
{
    JobElements myJobElements = GetJobElements(currentJob);

    Parallel.For(0, myJobElements.Length, (currentIndex, loopState) =>
    {
        if (MyFunction(param1, myJobElements[currentIndex]))
        {
            loopState.Stop();
        }
    }
    );
}
);

这是MyFunction的伪代码:

private bool MyFunction(MyObject1 param1, MyObject2 param2)
{
    log(string.Format("start SubFunction1() from thread {0}", Thread.CurrentThread.ManagedThreadId));
    SubFunction1(); //which uses System.Diagnostics.Process to start a batch file (.bat) to execute a Perl script. If successful, a file will be generated.
    log(string.Format("end SubFunction1() from thread {0}", Thread.CurrentThread.ManagedThreadId));

    log(string.Format("start SubFunction2() from thread {0}", Thread.CurrentThread.ManagedThreadId));
    SubFunction2(); //which again, uses System.Diagnostics.Process to start another batch file (.bat) to execute a Perl script which transforms the file from step #1 to a new file.
    log(string.Format("end SubFunction2() from thread {0}", Thread.CurrentThread.ManagedThreadId));

}

日志显示一个线程启动了SubFunction1()但从未完成;也就是说,没有日志条目说“end SubFunction1()”具有相同的线程ID。实际上,同一个线程似乎转移到数组中的下一个作业并再次调用SubFunction1()。当另一个线程试图让前一个线程启动SubFunction2()并且找不到从SubFunction1()生成的文件时发生异常。

我认为每个线程都保证从头到尾完成任务,我无法弄清楚为什么日志会以这种方式显示。我还要补充说,行为不一致;也就是说,程序运行的大部分时间没有例外,但有时由于上述问题而抛出异常。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

  

我认为每个线程都保证从头到尾完成任务,我无法弄清楚为什么日志会这样显示。

如果SubFunction1()抛出异常,则不会发生这种情况。

答案 1 :(得分:0)

  

我认为每个线程都保证从头到尾完成任务   完成,我无法弄清楚为什么日志显示这种方式

首先,由于您使用的是loopState.Stop();,因此无法保证,并且您的代码中不清楚MyFunction()返回的时间,时间和内容。

然后,做什么任务/线程以及写入日志(文件)的内容是两回事,而不是相互反映:

  • 如果日志写入同一文件,则最有可能的是日志使用单独的线程进行写入。然后log(string.Format("end SubFunction1() from thread {0}", Thread.CurrentThread.ManagedThreadId));不显示实际任务的线程ID(但是日志的线程)。如果它确实显示了实际任务的线程ID,那么就不清楚(从你的代码中)为什么你没有通过从多个线程写入日志来获得跨线程异常
  • 无论如何,写入同一个文件应该已经同步,否则重新使用同一个线程的下一个任务会覆盖先前任务的输出(缓冲区内容和/或文件内容)并不奇怪。

您应该通过以下任一方式同步日志记录:

  • 使用输出到数组(线程安全)及其在任务完成后的日志记录
  • 将您的日志包装到TPL数据流ActionBlock中(或者,使用带有队列的TP,同样如此)
  • 在每个循环结束时使用Parallel.For循环刷新输出流中的锁来保护记录