5

The following code:

while (x == 1) { ... }

might be optimized to

while (true) { ... }

if x gets assigned in another thread only. See Illustrating usage of the volatile keyword in C# . The answer there solves this by setting x to be volatile.

However, it looks like that is not the correct way to go about it according to these three contributors (with a combined reputation of over 1 million :) )

Hans Passant A reproducable example of volatile usage : "never assume that it is useful in a multi-threading scenario."

Marc Gravell Value of volatile variable doesn't change in multi-thread : "volatile is not the semantic I usually use to guarantee the behavior"

Eric Lippert Atomicity, volatility and immutability are different, part three : "I discourage you from ever making a volatile field."

Assuming a Backgroundworker is treated like any other multithreading (as opposed to having some built-in mechanism to prevent optimization) - How can bad-optimization be prevented?

Community
  • 1
  • 1
ispiro
  • 26,556
  • 38
  • 136
  • 291
  • 1
    Your question is unclear without context. Please show a specific example. – Jim Mischel Aug 23 '13 at 13:37
  • @JimMischel See the first link in my question (starting with "Illustrating usage..."). – ispiro Aug 23 '13 at 13:38
  • Not clear how use in a Backgroundworker is just like the link. Please post code. Do you have code that is not actually reading? – paparazzo Aug 23 '13 at 14:05
  • @Blam If test.foo is changed in a `Backgroudworker`'s `DoWork` method instead of by a `new Thread`. (This is actually a side point. The main point is - what should be used instead of volatile.) – ispiro Aug 23 '13 at 14:09
  • @ispiro: Correct way of accessing memory from different threads is to protect it with memory barriers, e.g. with some appropriate `lock`. A better approach would be to avoid shared state, as Eric recommends. – Vlad Aug 23 '13 at 14:11
  • 1
    @Vlad Exactly why I am asking for code. What is the problem OP is trying to solve? Why would you use that design when Backgroundworker supports cancellation. – paparazzo Aug 23 '13 at 14:14
  • @Blam How do I pass information to a Backgroundworker? Not only tell it to cancel, I mean - anything else? – ispiro Aug 23 '13 at 14:15
  • @Vlad What would a `lock` help? I'm not talking about what happens when the program runs, only about the optimization that might happen *before*. – ispiro Aug 23 '13 at 14:17
  • -1 If you would post a code sample of what you are trying to do then we could address the question. The while (x == 1) in that link you call problem statement can be achieved with Cancellation. – paparazzo Aug 23 '13 at 14:18
  • @ispiro: AFAIK it must be exactly enough, as read/write optimizations should not cross memory barriers; however Eric is a better source of information about C# anyway. – Vlad Aug 23 '13 at 14:22
  • @Vlad Thanks for the help. And for your deleted comment with the Backgroundworker code. (I was thinking about the Backgroundworker altering the variable, but it's the same idea the other way around.) – ispiro Aug 23 '13 at 14:51

1 Answers1

13

You're mischaracterizing my position slightly. Making the field volatile will prevent the optimization. Volatile is not broken in C#. (C++, that's another story.)

My point is not that volatile does not work as expected. My point is that reading and writing the same variable on two different threads is a bad idea in the first place. If you have to use volatile to make your program correct, consider redesigning your program so that it doesn't need volatile at all.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • I'm not "mutating the same variable on two different threads". Just like in [the link I provided](http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp/1284007#1284007) - one thread writes, the other reads. I just want to make sure that the reading thread actually reads (as opposed to the reading being "optimized-out"). – ispiro Aug 23 '13 at 13:55
  • 2
    Ok, same difference. Shared memory is a bad idea. If you can avoid it, do so. – Eric Lippert Aug 23 '13 at 14:08
  • So how do I pass information **to** the Backgroundworker while its running? (passing **from** can be done by ProgressChanged.) – ispiro Aug 23 '13 at 14:14
  • 1
    Imagine the background worker was a process, not a thread. How would you pass information to it? – Eric Lippert Aug 23 '13 at 14:16
  • Probably a MemoryMappedFile. Is there no simpler way to pass information (safely) to a Backgroundworker? – ispiro Aug 23 '13 at 14:19
  • And now you're sharing memory again. Is that the only way to get information from one place to another? – Eric Lippert Aug 23 '13 at 14:22
  • 1
    I'm not such an expert (to say the least). How _should_ it be done? – ispiro Aug 23 '13 at 14:23
  • Suppose the background worker was in another process on another machine. Now how would you pass information to it? – Eric Lippert Aug 23 '13 at 14:23
  • My only (little) experience with inter-computer communications uses `WebRequest`. That doesn't look like the way to go here, right? – ispiro Aug 23 '13 at 14:26
  • @ispiro: See [What is the simplest method of inter-process communication between 2 C# processes?](http://stackoverflow.com/q/528652/18192) . Or just cancel the `BackgroundWorker` and make a new one, though that's often too expensive. Found that page by googling "interprocess communication." Luckily, the first SO result was about c#. – Brian Aug 23 '13 at 14:31
  • @Brian So the simplest (safe) way to pass information to a BackgroundWorker is through WCF (or Anonymous pipes)? I thought it'd be a little simpler. Oh well... :) Thanks. – ispiro Aug 23 '13 at 14:38
  • @ispiro: If you wanted, I guess you could use sockets instead. My intuition is that pipes are a better choice. – Brian Aug 23 '13 at 14:42
  • @Brian Thanks for the help. I'm looking into all of those now. (Looking for the simplest.) – ispiro Aug 23 '13 at 14:45
  • @EricLippert Thanks. (I didn't know there might be an issue with one thread reading and the other thread writing.) – ispiro Aug 23 '13 at 14:46
  • 4
    @ispiro: My point is that there are lots of ways to share data between what are logically two processes that do not involve one of them saying "here's an address; watch it until it changes". Draw your inspiration from one of them. Or, better, **don't have a background worker that requires consuming new information all the time**. If new information comes in, as Brian suggests, *cancel the existing background worker* and *start a new one with the new information*. There are many ways to solve this problem. – Eric Lippert Aug 23 '13 at 14:52
  • 2
    @ispiro: Basically the intuition I want to convey here is: if your worker is logically a *server*, akin to a web server, then *make a client-server interface to it*. Web servers do not share memory with their clients; they have a well-defined interface between the client and the server. If by contrast your worker is just a drone doing some task and signaling when it is done, then don't use a background worker directly. Make a `Task` and have the Task Parallel Library handle the progress, result reporting and cancellation. – Eric Lippert Aug 23 '13 at 14:55
  • 1
    If you're just trying to implement a producer-consumer model, something like [`BlockingCollection`](http://msdn.microsoft.com/en-us/library/dd267312.aspx) is easier. – Brian Aug 23 '13 at 15:00
  • @Brian - I wondered how long I would have to wait until the obvious solution presented itself in place of sockets, pipes etc. – Martin James Aug 23 '13 at 15:07