2

In Java, we can't share a local variable between threads unless the final keyword is added. But in C#, it's allowed to write like this:

void DoSomeJob() {

    bool isDone = false;

    new Thread( ()=> {
        // Do some background job

        isDone = true;
    }).Start();

    while (isDone == false) {
        // Do some foreground job
    }
}

Actually, this worked in my simple tests.

-As you see, no Thread.Sleep() call or anything similar which causes reloading.

-Ran in both debug and release mode.

If I had to share a variable between threads (without any locks) like this, I would define it as a static variable with the volatile keyword to prevent running into an infinite loop. So I wonder if just simply reading a local variable like above will always work.

Btw, this is just a question out of curiosity, but not about writing better-multithreaded code.

Jenix
  • 2,996
  • 2
  • 29
  • 58
  • Does this answer your question? [How do I access variables from a different thread?](https://stackoverflow.com/questions/17870314/how-do-i-access-variables-from-a-different-thread) – Nicholas Hunter May 05 '21 at 12:54
  • 6
    Strictly speaking no, that's unsafe. (Note, that local variable is lifted into a field on a compiler-generated class, so the answer is the same as with an unsynchronised field access). While you'll *probably* be fine, there's no guarantee that the write done by your thread will be read by your foreground job (this can pop up particularly due to cache coherency on NUMA architectures) – canton7 May 05 '21 at 12:55
  • Basically yes. The only situation where it will fail is when the variable you're trying to change is property of UI related code (ie. you want to change visibility of a control). There you have to use a dispatcher that dispatches your call to a UI thread. The issue that you may get is with race conditions and those you have to deal with yourself (but it's out of the scope of your question ;-) ) – Lukasz Nowakowski May 05 '21 at 12:55
  • 3
    [SharpLab showing your local being lifted into a field](https://sharplab.io/#v2:CYLg1APgAgDABFAjAOgCoAsBOBTAhsASwDsBzAbgFgAoKAZgQCY4BhOAb2ri4QBY4ARAPYBlQQFtsAKUEAjABQBKdtU7c1MwYIA2cAgGchRbHAC8cAGa4te7JSqq1XIwHc4GHPjlxFJgHzKqRyC4AHoQgUE4PXFjGVwAYwBrEkxBAFciYDgAK1kVQOC1fUNjMwAXTDTbB0cAXwVkYTLcTDLFOxq1Z3QCLWM5YsEjUzNLa2wlDgLCrjCIqJiLQRwU9MycvOm6mtrqWqA=) – canton7 May 05 '21 at 12:56
  • 3
    Note that the [`volatile` keyword is not so hot either](http://joeduffyblog.com/2010/12/04/sayonara-volatile/) (11 years old, but mostly still relevant). Prefer the methods of the `Volatile` class to be explicit about what barriers you need (`Volatile.Read`, `Volatile.Write`) -- that is, assuming you don't just use `lock` and/or events, since optimizing threaded access is best left to library implementers who know a lot more about it than you do. And by that I mean generic "you", including me, since I've seen enough of the complexities to know I don't want to take my chances. – Jeroen Mostert May 05 '21 at 13:03

2 Answers2

-3

This example is probably fine HOWEVER you never know for sure.

The fact that it is a local variable does not matter. Always protect access to a variable that is used from >1 thread by using lock. Even if one thread only reads the value.

Not sure if useful, but, if what you want to do is test for a thread being complete, there are other ways to do this that are better, e.g. event handles, using join, or using tasks and wait.

This example has nothing to do with accessing UI controls from another thread, which while true, is a separate topic all together, and more of a design constraint due to the way that windows works.

mikelegg
  • 1,197
  • 6
  • 10
  • _"by using lock"_ - well, there are more possible ways than `lock`, but I am being nitpicky, I guess. – Fildor May 05 '21 at 13:17
-3

You should always keep one thing in mind when doing multithreading: as long as a piece of code is not atomic, it maybe interupted by another thread.

Assigning a bool value is atomic, but together with calculations before it its not.

Takeing your code as example, when the executions in the new thread is done but isDone bool indicator value is not updated yet, the main thread maybe just checking isDone and found it false. However in fact the executions in new thread was actually done therefore the main thread is doing wrong thing due to early reading on the isDone indicator bool value.

Your code will be fine if the code in main thread's loop works okey regardless of whether its isDone or not.

martinrhan
  • 362
  • 1
  • 18
  • 1
    The code may not be fine -- the main thread may never realise that the background job is done, because the write to `isDone` may never be seen by the main thread – canton7 May 05 '21 at 13:34
  • @canton7 why can't the main thread observe the change of isDone? – martinrhan May 05 '21 at 13:35
  • 1
    Because the read and the write aren't protected by memory barriers: when the write is done, this may only be stored in a cache somewhere and there's no guarantee that it's propagated to other bits of memory. Likewise the read may come from a cache, and there's no guarantee that the cache is up to date. On x86 you'll normally be OK, because the processor will normally flush the relevant caches, but on other architectures like ARM, and especially things with segregated memory like NUMA, the write might be stuck in one cache, and never propagated to somewhere the read can see it – canton7 May 05 '21 at 13:38
  • The example code isn't practical at all, and I would never write like that in real projects. The question was just about the visibility of a local variable between threads, but not atomicity. As said in the original post, Java doesn't allow this in the first place, but C# does and it seemed fine in simple tests, so I asked but was wrong. – Jenix May 05 '21 at 13:55
  • thanks to comments written here. I acknowledge that I am actually still quite lack of some understandings to C# and this answer is indeed not well considered before its made. – martinrhan May 05 '21 at 14:04
  • @Jenix Java would let you define a class which has an `isDone` field on it though (the class instance would be stored in a `final` variable, but the class would be mutable), and the calling thread and worker thread would both be able to access that field. This is exactly what the C# compiler is doing, see my SharpLab link above – canton7 May 05 '21 at 14:11
  • @canton7 Yeah, the `final` keyword. I said that in my original post. The reason why I thought C# was different for a moment was, a local variable doesn't need to be `const` or `readonly` like in Java. – Jenix May 05 '21 at 14:14
  • 1
    Right, because the C# compiler will lift that local into a field on a compiler-generated class, which the Java compiler won't do. The end result of that lifting process is something which you could write by hand in Java, though – canton7 May 05 '21 at 14:16
  • @canton7 Thank you. I didn't know the lifting process by the C# compiler existed. Of course, I would never think it's safe to share a local variable if I knew it haha – Jenix May 05 '21 at 14:30
  • See [this comment](https://stackoverflow.com/questions/67401715/is-it-safe-to-read-a-local-variable-in-multithreaded-programming-in-c#comment119135192_67401715), from an hour ago :) – canton7 May 05 '21 at 14:31
  • @canton7 I saw that but stackoverflow didn't let me just say thank you.. – Jenix May 05 '21 at 14:33