.NET 3.5 上的并行 - 线程未完成嵌套 For 中的任务

发布于 2024-12-04 20:28:18 字数 1650 浏览 1 评论 0原文

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

下面是并行任务的一个片段:

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() 但从未完成;即,没有日志条目显示具有相同线程 ID 的“end SubFunction1()”之类的内容。实际上,同一个线程似乎继续执行数组中的下一个作业并再次调用 SubFunction1()。当另一个线程试图代替前一个线程启动 SubFunction2() 并且找不到从 SubFunction1() 生成的文件时,就会发生异常。

我认为每个线程都保证从头到尾完成其任务,但我不明白为什么日志会这样显示。我还应该补充一点,行为并不一致;即,大多数情况下程序运行时没有异常,但有时会由于上述问题而引发异常。

有什么想法吗?

I downloaded Parallel Extensions for .NET 3.5 SP1 on DevLabs a while ago and started using it. Everything seemed to work fine until I noticed some exceptions in the logs which I have a hard time to understand why they happened.

Here's a snippet of the parallel task:

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

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

Here's the pseudo code of 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));

}

The logs showed that one thread started SubFunction1() but never finished; i.e., there was no log entry saying something like "end SubFunction1()" with the same thread ID. Actually, the same thread seemed to move on to the next job in the array and call SubFunction1() again. The exception happened when another thread tried to stand in for the previous thread to start SubFunction2() and couldn't find the file generated from SubFunction1().

I thought each thread was guaranteed to finish its tasks from start to finish and I can't figure out why the logs show this way. I should also add that the behavior is not consistent; i.e., most of the time the program runs with no exceptions but sometimes it throws exceptions due to the aforemention problems.

Any thoughts?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

书间行客 2024-12-11 20:28:18

我认为每个线程都保证从头到尾完成其任务,但我不明白为什么日志会这样显示。

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

I thought each thread was guaranteed to finish its tasks from start to finish and I can't figure out why the logs show this way.

This will not happen if SubFunction1() throws an exception.

时光沙漏 2024-12-11 20:28:18

我认为每个线程都保证从开始到完成其任务
完成,我不明白为什么日志显示这样

首先,不能保证,因为您正在使用 loopState.Stop(); 并且从您的代码中不清楚如何、何时以及什么MyFunction() 返回。

然后,执行任务/线程的内容和写入日志(文件)的内容是两个不同的事情,很可能无法相互反映:

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

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

  • 使用数组的输出(这是线程安全的)以及任务完成将
  • 日志包装到 TPL 后的日志记录数据流 ActionBlock(或者,使用带有队列的 TP)
  • 在 Parallel.For 循环内使用锁,在每个循环结束时刷新输出流以保护对日志的访问

I thought each thread was guaranteed to finish its tasks from start to
finish and I can't figure out why the logs show this way

First, it is not guaranteed since you are using loopState.Stop(); and it is not clear from your code how, when and what MyFunction() returns.

Then, what is doing tasks/threads and what is written to a log (file) are two different things, more than probably not reflecting each other:

  • If log writes to the same file then it is most probable that log is using a separate thread for writing. Then log(string.Format("end SubFunction1() from thread {0}", Thread.CurrentThread.ManagedThreadId)); does not show the actual task's thread ID (but log's thread). If it does show actual task's thread ID, then it is not clear (from your code) why you doesn't get cross-thread exception by writing to log from multiple threads
  • Anyway, writing to the same file should have been synchronized, otherwise it is not surprising that the next task re-using the same thread overwrites the output of previous task (both in buffer content and/or file content)

You should have synchronized the logging by either:

  • using output to an array (which is thread safe) and its logging after task finishes
  • wrapping your log into TPL dataflow ActionBlock (or, what is the same, using TP with a queue)
  • using lock inside Parallel.For loop flushing output stream at the end of each loop to protect acceess to log
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文