4

I came up with an interesting situation. I have a bool variable and then I want multiple threads to perform its own independent tasks and then mark that bool depending on the thread's result. This variable is never read by any of the threads and is never used before all writing tests have finished. Like this:

public bool F(){
    bool flag = false;
    Parallel.ForEach(list, element => 
    {
        bool result = // Do independent work here;
        if (result) flag = true;
    });
    return flag;
}

Notice how I never read flag inside my Parallel.ForEach. But what could happen is having multiple threads attempting to write true to flag (but never false). Is it safe to do this?

Derek Patton
  • 223
  • 2
  • 8
  • 2
    If the value is never read, then who cares. Does a tree falling in an uninhabited forest make a sound? Better to move this question to a Philosophy Q&A. – Richard Schneider Jan 25 '15 at 02:19
  • 2
    @RichardSchneider it's never read in the context of the `ForEach()` but it is afterwards when it is returned by the method. – Erik Philips Jan 25 '15 at 02:21

3 Answers3

5

Yes, this is absolutely safe to do. The only thing that could happen is multiple threads writing true into flag concurrently, so you don't know which thread would end up overriding what result, but the end result is going to be the same.

By the time you read the flag all the writing has finished, and you never attempt to write anything except true into flag. Hence, the only two situations that you could see are as follows:

  • None of the threads write anything into flag - in this case the flag would remain false, or
  • One or more threads write true into the flag - in this case the flag would be set to true.

If you want to avoid this situation altogether, and perhaps save some execution time, you could do this:

return list.AsParallel().Any(element => {
    bool result = // Do independent work here
    ...
    return result;
});

This code will not result in equivalent execution path, because the execution may stop early if one of the threads returns true. If this is not desirable, keeping your ForEach approach is fine, too.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • What about memory visibility? Isn't there a chance of returning `false` even when one or more threads has set `flag` to `true`? – adv12 Jan 25 '15 at 02:21
  • @adv12 There's no chance of that happening, because by the time the `flag` is read every last thread will have finished execution. – Sergey Kalinichenko Jan 25 '15 at 02:23
  • 1
    What if all threads are on separate processors, and even though each thread runs to completion, the `true` values have only been written in a processor cache and haven't been flushed to main memory? (This may just be paranoia on my part; perhaps something about Parallel.ForEach guarantees this won't happen, or maybe I just don't get threading.) – adv12 Jan 25 '15 at 02:25
  • 1
    @adv12 .NET guarantees that all side effects, including writing to variables, are complete by the time the method returns. – Sergey Kalinichenko Jan 25 '15 at 02:27
3

You're reading from flag when you return it. Because there's no locking, there's no guarantee that the thread on which F() is called will read the most up-to-date value. It's possible (though admittedly unlikely) that the updated value of flag is sitting in a processor cache that won't be flushed without a memory barrier of some kind.

Edit: in comments on @dasblinkenlight's answer, he says that ".NET guarantees that all side effects, including writing to variables, are complete by the time the method returns." In that case this answer is wrong. Based on his rep and my continuing uncertainty about threading, I'd be inclined to believe him, but the answers to some questions I've asked here have left me pretty nervous about memory visibility. So I don't know. Don't take this answer with any more certainty than it deserves.

Community
  • 1
  • 1
adv12
  • 8,443
  • 2
  • 24
  • 48
0

There is nothing wrong with your strategy at a fundamental level. The simultaneous writes to flag will not be an issue strictly because of the limited semantics of how you are using it. The real question here is whether the final read of flag at the end of the method is safe. In this particular scenario it probably is, but only by accident. It is likely that Parallel.For is injecting a memory barrier without your knowledge that prevents the thread from caching the false value or otherwise reading a stale value. I do recommend, however, that you get in a habit of making your intentions know by explicitly placing a call to Volatile.Read (or any other memory barrier generating mechanism) when reading the variable.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150