5

Imagine I have this code where inside Windows forms timer I can spawn some threads - but I ensure that ONLY one thread is running using following approach (as indicated by one of the answers from here - by Matt Johnson):

nb: let's assume for now this _executing approach works and I don't use backgroundworker, etc.

private volatile bool _executing;

private void TimerElapsed(object state)
{
    if (_executing)
        return;

    _executing = true;

    if(smth)
    {
    Thread myThread = new Thread(MainThread1);
    myThread.IsBackground = true;
    myThread.Start();

    }else
    {
    Thread myThread = new Thread(MainThread2);
    myThread.IsBackground = true;
    myThread.Start();
    }
}


 public void MainThread1()
 {
   try
   {
     methodWhichAddelementTomyList(); // e.g., inside list.add();
   }
   finally
   {_executing = false;}
 }
 public void MainThread2()
 {
   try
   {
     methodWhichAddelementTomyList(); // e.g., inside list.add();
   }
   finally
   {_executing = false;}
 }

Now I also have List instance variable, which you can see I access from MainThread1 and MainThread2 - but since my logic above I ensure that MainThread1 and MainThread2 never run in parallel, do I still have to make the list volatile? Can I encounter issues related to caching the list variable?

EDIT: And also does this approach protect me from running those threads in parallel? (The answer in the linked question is a bit different -it runs the work inside timer - so I want to double check).

EDIT2: Honestly there is no common opinion below whether I should apply volatile keyword on my list object or not. This state of affairs confuses me. So documented answer is still welcome; otherwise this is not fully answered

Community
  • 1
  • 1
  • @Cicada: So every time I create instance variable which I think can be accessed by two or more threads - even if I am sure those threads never run in parallel - I should consider making that instance variable volatile? (if threads run in parallel volatile might not be enough right?) –  May 14 '15 at 10:35
  • @Cicada: But if my ivar is going to be used only within the same form (no additional threads) or other form - I should not care to make it volatile? –  May 14 '15 at 10:41
  • 2
    Just use `lock`s whenever accessing shared state. Especially in a case like this (assumed "no contention"), they're well worth it. Lock-less code is much harder than simple multi-threaded code, and multi-threaded code is already hard enough to cause issues all over the place. Or just get rid of the shared state - perhaps you could simply have the parallel operation return a value that you'll add to the collection on the GUI thread, for example? – Luaan May 14 '15 at 11:12
  • @Luan: `_blacklist` is my list variable - the shared one as here: http://codepad.org/1SzesVYj. You say: (1) Applying lock to it and (2) using my `private volatile bool _executing;` approach for preventing two threads from running should be sufficient? I would very much appreciate if someone can show samples how to apply locks on the codepad code as I am a bit new to C# –  May 14 '15 at 11:16
  • @Luan: and aside why should I worry about locks if I have ensured the two threads never run in parallel that is also weird ... –  May 14 '15 at 11:19
  • Well, for one, you have three threads (at least), not two - don't forget about the UI thread. Second, if you never want to run the two threads in parallel, *don't use multi-threading*. You're adding complexity for no reason whatsoever. Third, if you use a lock, you don't need the `_executing` flag - `lock`s are exclusive, so you'll be *guaranteed* the code inside the `lock` doesn't run simultaneously. Fourth, if you use `System.Threading.Timer`, the `Elapsed` event handler already runs on a new thread, no need to start another one afterwards. – Luaan May 14 '15 at 11:50
  • @Luan: This is a windows forms timer. Let's assume I am using _executing flag. Now the question is what to do with list? Make it volatile? Put locks around it? –  May 14 '15 at 11:54
  • And it seems that you want to periodically run something *only if no other thing is processing* - the easiest way to do that would be a simple `System.Threading.Timer` without a period, resetting the timer manually after the processing is done. But you still need a `lock` around the access to the shared list - you're still accessing the same mutable list from multiple threads. It doesn't matter if they run in parallel or not (and they probably do - who's *reading* the list?). – Luaan May 14 '15 at 11:54
  • @Luaan: This is a windows forms timer. Please see my above comment. Like I said let's assume I am using _executing flag. Now the question is what to do with list? Make it volatile? Put locks around it? –  May 14 '15 at 11:56
  • I see that you're using a windows forms timer, I'm just suggesting you don't have to. And as I've already said, put locks around the access to the list. But you have to put them in all the places where you read from the list as well, not just around the writes. – Luaan May 14 '15 at 12:05
  • @Luaan: I am already using it. Please see my discussion with usr –  May 14 '15 at 12:07
  • @Luaan: It seems seems I am ensuring no two threads run at the same time, no need for volatile? no need for lock? –  May 14 '15 at 12:17
  • But you're not *reading* that one list on the same thread, are you? It's not enough to ensure that the list can't be written to from two places at the same time - you also need to ensure that it can't be accessed in any way while writing. And even then, be careful about this - in general, don't use anything that's not explicitly thread-safe accross threads without synchronization. There's quite a few methods that write during a read operation (so they're not thread-safe even when "immutable"). – Luaan May 14 '15 at 12:21
  • " you also need to ensure that it can't be accessed in any way while writing" -> did I not ensure it using _executing flag? Anyway what is your recommendation? It seems you contradict with "usr" too as below? Use _executing flag only doesn't seem enough for you? –  May 14 '15 at 12:24
  • @Cicada: Do you have some explanation why your opinion - that I should use volatile on List, and usr's opinion - see the answer and discussion below, differ? This is confusing me –  May 14 '15 at 21:29

3 Answers3

3

I'll restate your question:

If I ensure two threads never run in parallel do I still have to make my list variable volatile?

You don't have two threads, you have three: one thread that launches the two others. That one is always running in parallel of either other threads, and it uses a shared flag to communicate with them. Given that and the code you posted, it is not required to mark the list as volatile.


But in the case of two threads and two threads only, that would somehow execute one after each other without interference from a third (i.e, reading from a shared variable), making the list volatile would be enough to guarantee that the two threads always see the same data.

For two threads that do not run concurrently to see the list in a consistent state (in other words, up-to-date), they always have to work on the latest version of what resides in memory. This means that when a thread starts using the list, it has to read from the list after the previous writes have settled.

This implies memory barriers. A thread needs an acquire barrier before using the list, and a release barrier after being done with it. Using Thread.MemoryBarrier, you can't control the semantics of barriers that finely, you always get full barriers (release and acquire, which is stronger than what we need), but the end result is the same.

So, if you can guarantee that the threads never run in parallel, the C# memory model can guarantee that the following works as expected:

private List<int> _list;

public void Process() {
    try {
        Thread.MemoryBarrier(); // Release + acquire. We only need the acquire.
        _list.Add(42);
    } finally {
        Thread.MemoryBarrier(); // Release + acquire. We only need the release.
    }
}

Notice how list is not volatile. Because it's not needed: what is needed is the barriers.

Now the thing is, the ECMA C# Language Specification says (emphasis mine):

17.4.3 Volatile fields

  • A read of a volatile field is called a volatile read. A volatile read has "acquire semantics"; that is, it is guaranteed to occur prior to any references to memory that occur after it in the instruction sequence.

  • A write of a volatile field is called a volatile write. A volatile write has "release semantics"; that is, it is guaranteed to happen after any memory references prior to the write instruction in the instruction sequence.

(Thanks to R. Martinho Fernandes for finding the relevant paragraph in the standard!)

In other words, reading from a volatile field has the same semantics as an acquire barrier, and writing to a volatile field has the same semantics as a release barrier. Which means that given your premise, the following code stanza behaves identically1 to the previous one:

private volatile List<int> _list;

public void Process() {
    try {
         // This is an acquire, because we're *reading* from a volatile field.
        _list.Add(42);
    } finally {
        // This is a release, because we're *writing* to a volatile field.
        _list = _list;
    }
}

And that's enough to guarantee that as long as both threads do not run in parallel, they will always see the list in a consistent state.

(1): Both examples are not strictly identical, the first one offers stronger guarantees, but these strong guarantees are not required in this specific case.

Community
  • 1
  • 1
user703016
  • 37,307
  • 8
  • 87
  • 112
  • Are you contradicting yourself? First you said this: "You don't have two threads, you have three: one thread that launches the two others. That one is always running in parallel of either other threads, and it uses a shared flag to communicate with them. Given that and the code you posted, it is *not* required to mark the list as volatile.". Then in the end of the answer you still say I need to make the list as volatile ?? –  May 15 '15 at 05:28
2

Making the object reference to the list volatile does nothing to the list itself. It affects the guarantees you get when reading and assigning to that variable.

You can't apply volatile somewhere and expect it to magically make a non-thread-safe data structure thread-safe. If it was that easy threading would be easy. Just mark everything volatile. Doesn't work.

It appears from the code and description given that you are accessing the list on one thread only. That does not require synchronization. Note, that even if you read a list on a second thread that is unsafe. If there is at least one writer there cannot be any other concurrent access. Not even reads.

Here's a simpler approach:

Task.Run(() => Process(smth));

 ...

 public void Process(bool smth)
 {
   try
   {
     if (smth) methodWhichAddelementTomyList();
     else otherThing();
   }
   finally
   {_executing = false;}
 }

No more "two threads". That's a confusing concept.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Why is it full of races - it is using WIndows Forms timer. And then if so why is the answer in the linked question (by Matt Johnson) with so many upvotes? My approach is similar to his? –  May 14 '15 at 11:27
  • Is the answer by Grx70 in that linked question using background worker safer for preventing the two threads to run in parallel? –  May 14 '15 at 11:27
  • "It appears from the code and description given that you are accessing the list on one thread only" - Yes, if I ensure I only run one thread at a time - you say I still needs locks on the list variable? (Then it remains which approach to take when I want to prevent that the timer doesn't spawn two threads - currently I am doing it with volatile flag) –  May 14 '15 at 11:28
  • Can you please provide some solution instead of navigating to other options? I am getting quite confused -please see my above comments. (1) Which approach should I use in my above code to prevent two threads from running in parallell? You say answer by Matt Johnson is not fine? Is the answer by Grx70 better? (in the linked question - http://stackoverflow.com/questions/12570324/c-sharp-run-a-thread-every-x-minutes-but-only-if-that-thread-is-not-running-alr?lq=1). (2) Do I still need locks on the list variable ?? –  May 14 '15 at 11:35
  • Races: I overlooked that this is a Winforms timer. This is safe, my mistake. WinForms timer ticks are serialized. – usr May 14 '15 at 11:40
  • Thank you I am glad. But then do I still need locks on the list variable is this what you said ?? –  May 14 '15 at 11:42
  • BackgroundWorker is not "safer". Satefy is not relative. It's either safe or not. Since there is only one thread this is safe. Consider using Task, though. It's a nicer API.; Neither volatile nor lock influences what the list does. It seems you don't understand this particular thing. lock provides mutual exclusion for a region of code. Since there is only one thread there is no need for mutual exclusion. No need for lock. There is no other thread to "exclude". – usr May 14 '15 at 11:42
  • You don't need a solution. Your code is fine. I don't see how this could end up running two threads in parallel. – usr May 14 '15 at 11:43
  • Thanks. In one project I solved this using Bacgroundworker. That is also fine right? Ok here I will use it as it is using `private volatile bool _executing;`. But what about locks? Or making the list volatile? I've heard `list` can be cached that is why I asked about volatile. So you say I neither need volatile keyword nor locks? (this would contradict some comments above) but I am interested in your opinion. Thanks! –  May 14 '15 at 11:45
  • 1
    You need volatile on _executing but not on the list. If you are toying with threading you should understand under what exact circumstances synchronization is required. Short answer: When multiple threads access the same data structure and at least one of the is a writer. This is not the case here. Do you understand why? – usr May 14 '15 at 11:49
  • I understand because using _executing I have made sure no two threads run in parallel. But I have read compiler might cache value of `list` if it is meant to be accessed by two threads so the value you get one time might not be its real value - also see so many different answers and comments above? I find it weird people have different opinions this is programming not black magic right? Anyway so you say `list` needs nothing because I have made sure two threads don't run in parallel right? –  May 14 '15 at 11:53
  • The way you access `list` is not visible in the question...; "meant to be" What does that mean? What is meant to be does not matter. What matters is what access factually occurs. The runtime will always execute your code so that single-threaded execution behaves like you expect. There is only a single thread. Whatever caching there is - you can never observe it. It would be against the specification of the C# language and the CLR. This is a very easy case to reason about. One thread implies that there are no threading concerns. – usr May 14 '15 at 11:57
  • `_blacklist` is my list object actually as here: http://codepad.org/1SzesVYj. And the threads can call these methods sometimes. So you say I don't need to touch the list? Not make it volatile? No locks? Just use approach with `private volatile bool _executing;` ? –  May 14 '15 at 12:00
  • Yes, just that. There is a bug in DeleteOldFilesFromBlacklist btw. Calling RemoveAt causes the list indexes to change. You'll effectively skip one element. – usr May 14 '15 at 12:01
  • @user300455 I added some code to simplify this issue away. – usr May 14 '15 at 12:03
  • Thank you. Can you please clarify more what is the problem with `DeleteOldFilesFromBlacklist` and how to correct that? I will stick with my old approach I am a bit new to C# and these `() => Process(smth)` things are confusing me. –  May 14 '15 at 12:06
  • Test DeleteOldFilesFromBlacklist with two elements in the list that should both be deleted. Only one of them will actually be deleted. Use the debugger to find out why. – usr May 14 '15 at 12:08
  • out of curiosity: Why is this not race with windows forms timer, and race with other timer? –  May 14 '15 at 19:48
  • Because timer ticks are serialized on the UI thread. Other timers can have multiple ticks concurrently. – usr May 14 '15 at 19:58
  • just wanted to let you know I am leaning towards your solution more. _executing flag to make sure threads don't run together. And no need for volatile and locks on List. Because as I asked Jeb below, since I have ensured no two threads run together, I don't see need for lock... about volatile not sure yet –  May 14 '15 at 20:58
  • 1
    Good. If you want skill development advice: Revisit this question in a few weeks and make sure you understand every statement that was made here. This stuff is foundational threading knowledge. It is dangerous to use technologies that you have a fuzzy understanding of. Shed that habit. – usr May 14 '15 at 21:03
  • Thanks for the skill I advice I will do this definitely. I am not clear why I don't need volatile when there is only one thread for example. The varying responses on this question confuse me too.... why people suggest other things... But yours made most sense to me and I agreed with it also –  May 14 '15 at 21:15
  • Cicada advised the opposite first comment :( –  May 14 '15 at 21:29
0

There seem to be 2 issues here:

  1. If you are using two threads, but they never run asynchronously, then why have two threads? Just serialize your methods appropriately, i.e. stick to one thread.

  2. However, if two threads is some sort of requirement (e.g. allow one thread to continue processing/remain unblocked whilst another is performing some other task): even though you have coded this to ensure no two threads can access the list at the same time, to be on the safe side, I would add a locking construct since a list is not thread safe. To me, it's the most straightforward.

You can use a thread safe collection for this instead, such as one of the collections in System.Collections.Concurrent. Otherwise, you'll need to synchronize all access to the List (ie: put every Add call within a lock),

I personally avoid using volatile. Albahari has a good explanation for it: "the volatile keyword ensures that the most up-to-date value is present in the field at all times. This is incorrect, since as we’ve seen, a write followed by a read can be reordered."

Volatile just ensures the two threads see the same data at the same time. It doesn't stop them at all from interleaving their reads and write operations.

E.g.: Declare a synchronisation object, e.g.:

private static Object _objectLock = new Object();

and use like so in your methodWhichAddelementTomyList method (and anywhere else your list is modified) to ensure serial access to the resource from different threads:

lock(_objectLock)
{
    list.Add(object);
}
Jeb
  • 3,689
  • 5
  • 28
  • 45
  • If I use `backgroundworker` to make sure no two threads run in parallel - what to do with the `list` now? Volatile still makes no sense? –  May 14 '15 at 10:51
  • Is the answer on the linked question by Grx70 for making sure no two threads run in parallel safer? and can you please provide sample using thread safe collections (if I don't use backgroundworker)? –  May 14 '15 at 10:58
  • Or can you please provide sample on how to apply `lock` on my list variable - assuming I have methods for adding elements to list, methods for removing elements for list, and sometimes I check if element is in the list ... otherwise your answer is now confusing me more than helping –  May 14 '15 at 11:03
  • Sorry for confusing you. I've edited my answer with an example. Hopefully makes sense? – Jeb May 14 '15 at 11:08
  • 1
    As a fundamental rule: "if you have two or more threads," always use thread-safe techniques. That means: locking and unlocking *anything* that the two threads have in common. Every time. Otherwise, don't use threads. In the design that you discuss, where you have many threads but "only one of them is running at a time," huh, you probably don't need threads: you probably need a to-do list. That "Windows form timer" should probably be adding requests to a queue, not spawning threads. – Mike Robinson May 14 '15 at 11:09
  • @Jeb: `_blacklist` is my list object as here: http://codepad.org/1SzesVYj. Can you please show me the sample and apply lock in that code? I am a bit new to C# and would appreciate your help. So then I don't need to put lock when calling the method say `AddToBlacklist`? And finally these locks and my `private volatile bool _executing;` approach should be sufficient ? –  May 14 '15 at 11:11
  • So locks on list variable And my way of preventing two threads from running in parallel (e.g., using `private volatile bool _executing;`) approach should be Enough ? e.g. using both methods separately –  May 14 '15 at 11:22
  • If the code is to be extended in the future and there is a requirement for 2 threads to be running for whatever reason, this to me is the safest option. Un-contended locks only add a few nanoseconds to your program. – Jeb May 14 '15 at 13:21
  • Do you say if I use locks I should not use the `private volatile bool _executing;` anymore ?? But what if I still want to restrict the two threads from running in parallel ?? –  May 14 '15 at 13:37
  • it concerns me your explanation is not inline with usr's. Let's say i made sure that one thread doesn't run until other finishes using "private volatile bool _executing;" approach. Now, this way why should I still concern myself with locking the list? I know it is being accessed only by single thread at given point in time. And why bother with volatile too? –  May 14 '15 at 16:13
  • Why bother with more than one thread in the first place then? – Jeb Jun 02 '15 at 22:11
  • 1
    For what it's worth, I also shy-away from `volatile` ... simply because I can't *see* the interlocking that it's doing. "I *want* to see it." I want to be able to verify that the interlocking is being done exactly as I want. I also want it to be very obvious to any future colleague. Interlocking is (of course) critically important to any multi-threaded or multi-process application, and I always want to "eyeball it." While I appreciate what the C# language is trying to do for me, in this case I just don't want it. "But, maybe that's just me ..." – Mike Robinson Jun 04 '15 at 01:52