views:

265

answers:

9

The scenario

I have a class with a bool Enabled property, that is used by a loop on another thread to see whether it should stop or not. The idea is that a different thread can set that property to false and stop the other thread running cleanly.

The question

Should I bother to serialise access to that Enabled property using something like lock (lockObject) { ... }, or is it safe without?

+5  A: 

Primitive type reads are atomic provided that they fit within a CPU read. Thus a 32 bit primitive type read is atomic on a 32 bit CPU, whereas a 64 bit type read is not. However, unless it is also volatile your other thread may not see changes due to caching.

Brian Rasmussen
This is not entirely true. Reads/writes are only guaranteed to be atomic if the size of the value is less than or equal to the bitness of the CPU. Therefore, reads/writes of Int64s on a 32 bit machine will not be atomic.
Kent Boogaart
@Kent: I agree, which is why I added more info, but perhaps my update and your comment crossed each other. Is it still unclear?
Brian Rasmussen
@Brian: cool, it's clear now.
Kent Boogaart
Well I added a bit of detail anyway. Hopefully its better.
Brian Rasmussen
It also depends on the alignment of the memory. If a 32-bit value happens to be stored in two consecutive memory locations on a 32-bit machine, reads and writes will not be atomic.
Mehmet Aras
The other thread would see the change if its on the same CPU, but definately not if its on a different CPU. Volatile will work just fine.
Simon Hughes
@Mehmet: Correct, but I believe the compiler takes care of this.
Brian Rasmussen
The CLR guarantees all managed variables are word aligned. IF you mess about with pointers and explicit offsets all bets are off.
ShuggyCoUk
+3  A: 

I think you only need to mark the boolean variable as volatile. This will ensure that the thread that you wish to stop running always sees the most updated value of this boolean.

bruno conde
+3  A: 

If one thread read the value and one other thread write the value, in this case it is safe but pay attention to declare the Enabled property with the volatile keyword to be sure that the value will be synchronized to all processors if you run with a dualcore pc.

Coolweb
+1  A: 

If you don't use volatile or lock the boolean it will work x86. It may not work on Intel's Itanium CPUs (IA64) though. Here's a good codeproject kb article that explains it all.

RichardOD
If you don't use volatile or lock its OK, but ONLY on a single CPU. Multiple CPU's and your in trouble.
Simon Hughes
@Simon Hughes. That's not what the article says, or anything else I've ever read on the matter. Do you have documentation that shows this?
RichardOD
+3  A: 

As others already stated correctly, volatile can be used (or a lock on the variable) to access a variable's value in a thread safe manner.

Without making the variable volatile the compiler might apply optimization techniques that reorder instructions which might lead to unexpected and unpredictable results.

The relevant part of the C# language specification (10.4.3 Volatile fields) further states:

For volatile fields, such reordering optimizations are restricted:

  • 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.

These restrictions ensure that all threads will observe volatile writes performed by any other thread in the order in which they were performed. A conforming implementation is not required to provide a single total ordering of volatile writes as seen from all threads of execution. The type of a volatile field must be one of the following:

  • A reference-type.
  • The type byte, sbyte, short, ushort, int, uint, char, float, or bool.
  • An enum-type having an enum base type of byte, sbyte, short, ushort, int, or uint.
0xA3
+1  A: 

If you didn't mark it as volatile it will work on single CPU computers (because of 32 bit atomicity), but not PC's with multiple CPU's as it would hold the value in its on die cache and not retrieve the the very latest value, Hence you need to mark with Volatile. Its true you don't need to lock items if its 32 bits in size as its just a registry sized change and thus atomic.

There is an article on it here: www.yoda.arachsys.com/csharp/threads/volatility.shtml

There is also something called Synchronization Domains.

The synchronization domain provides automatic synchronization of thread access to objects declaratively. This class was introduced as a part of the infrastructure supporting .NET remoting. Developers wishing to indicate that a class is to have access to its objects synchronized must have the class inherit from ContextBoundObject and mark it with the SynchronizationAttribute like so:

[Synchronization]
public class MyController : ContextBoundObject 
{
  /// All access to objects of this type will be intercepted
  /// and a check will be performed that no other threads
  /// are currently in this object's synchronization domain.
}

Further information on Syncronisation domains can be found here: msdn.microsoft.com/en-us/magazine/dd569749.aspx

Simon Hughes
I'd be worried that the sync domain essentially means that all access to the object is serialised, and I don't need that in this case. Would it hurt performance or is it "clever" somehow?
Neil Barnwell
This seems like a pretty complicated solution for what looks to be a simple latch. I think dropping `volatile` in front of the variable declaration would be much easier (read: better).
Imagist
@Imagist: KISS rule. I am always amazed at how many people do not follow it.
Leahn Novash
A: 

You can use Interlocked.Exchange in this case, if you are concerned about an ugly lock.

DanDan
A: 

There is an MSDN example that describes exactly what you are tying to do: How to: Create and Terminate Threads (C# Programming Guide). It suggests that you need to declare your Enabled property as volatile and don't need to lock it.

Martin Brown
A: 

You could as people already written mark the variable with volatile or lock it.

However, what you're trying to accomplish (allowing threads to communicate with each other by signaling) is already built into the .NET framework.

Take a look at ManualResetEvent at MSDN.

Patrik
D'oh yes - I know about that one. Bad example on my part. I actually really have a situation when the "other thread" is looking at a value of a variable and using it in it's operations. At a non-deterministic time, a different thread may modify that value, and so the looping thread starts using that new value.
Neil Barnwell