3

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?

notlkk
  • 1,231
  • 2
  • 23
  • 40

2 Answers2

0

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
0

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.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373