Warning: Seriously geeky stuff ahead. May not be suitable for children or anyone who doesn’t love the minutia of JIT optimizations and the CLR memory model.
A reader, Russ, asks:
Given the importance of volatile in light of Greg’s comments here: http://blogs.msdn.com/grantri/archive/2004/09/07/226355.aspx. why doesn’t vb.net support a similar construct? or are we not allowed to write high-performance multithreaded code using vb? :p
The construct that Russ is referring to is volatile fields. C# allows you to declare field with the “volatile” modifier, which results in all accesses to the field being prepended in the IL with the opcode “volatile.” I’d like to pretend that I could adequately explain to you what volatile reads and writes actually do, but there is a high probability that I would get it wrong because the CLR memory model is not my thing. It is, however, Vance’s thing and you can check out a message he wrote that talks in more detail about the subject.
Although VB doesn’t support the volatile modifier, you’re not up a creek without a paddle. In fact, as I said above, marking a field as volatile in C# doesn’t do anything to the field itself – it just makes sure that the compiler prefixes all reads and writes with the volatile modifier. Volatility is really a property of reading and writing from a field, not a property of the field itself. In fact, there may be situations where you only care at certain points whether you do a volatile read or write, in which case the volitile field modifier is overkill (because it makes all reads and writes volatile). A slightly more ideal situation would be able to mark just a particular read or write as being volatile.
The CLR provides a way to do this, sort of. The type System.Threading.Thread provides VolatileRead and VolatileWrite methods that allow you to do volatile reads and writes from fields without having to do any declarative work at all. So, everything is right with the world, correct? Unfortunately, no. If you look at the definition of VolatileRead and VolatileWrite, they use reference parameters (ByRef) to refer to the field that you’re reading or writing from. This is necessary because if they were regular value parameters (ByVal) the VolatileWrite wouldn’t work at all and VolatileRead wouldn’t give you the desired behavior. (That’s because value parameters are copied onto the stack. That copy wouldn’t be a volatile read. When you pass the address of the value, then the VolatileRead method can do a volatile read of the value at that address.)
The problem is that when you pass a variable to a reference parameter, the type of the variable must exactly match the type of the reference parameter. If it doesn’t, then you have to create a temporary variable of the right type, copy the value into the temporary, call the method and then copy the value out of the temporary. This isn’t an issue for primitive types because there are overloads of VolatileRead and VolatileWrite for all the primitive types. But what about some random reference type like an array (which occurs in Grant’s example)? The closest overload is a ByRef parameter of Object, but that’s still not going to be an exact match. That means you fall into the copy-in/copy-out scheme I talked about a moment ago, which screws everything up because the reads and writes to the temporary variable are not volatile! Arrrrg!
So, are VB users just screwed? Fortunately, no. There’s another method on System.Threading.Thread that can help out here: MemoryBarrier. When you call MemoryBarrier before a write or after a read, you effectively make the read or write a volitile read or write. (In fact, in Whidbey this is how VolatileRead and VolatileWrite are implemented.) You could even write a generic VolatileRead and VolatileWrite to do this for you in VB 2005:
Function VolatileRead(Of T)(ByRef Address As T) As T
VolatileRead = Address
Sub VolatileWrite(Of T)(ByRef Address As T, ByVal Value As T)
Address = Value
So, yes, VB users are allowed to write high-performance multithreaded code… 🙂
Note that there is an error in Vance’s post:
Thanks for pointing that out, Jeroen!
Dunno what ur talking about Paul, but it’s nice to have you back!
Glad to learn this method. Thanks! But if volatile in C# is nothing special on the fields, why don’t allow VB code read or write them? I mean VB treate C# volatile fields unaccessable now.
thanks for answering my question!
Could someone explain bereifly and in simple form (oh i read dozens of article on this question but couldn’t understand none) , what does MemoryBarrier method do and why its place in VolatileRead and VolatieWrite (the one paul has suggested) is different ? (in VolatileRead it’s after the assignment, but vice-versa in VolatileWrite)
The best place to start I can find is maybe:
Ack. So there’s an error. Where specifically is the error? The response Vance gives is quite vague. And it is very frustrating to get 90% of an article explaining why things won’t work, then you find out there is an error ‘somewhere’ in there. The last TWO lines explain the code, not explaining MemoryBarrier at all, thus making this article something that someone spend a fair amount of time on, that really doesn’t explain the situation, which I assume is the author’s goal.
I appreciate the author’s time spent on this, but I wish someone had spent the extra 5 minutes fixing the error, instead of a vague reference using pronouns like ‘it’ and such, and spend 10 minutes more explaining MemoryBarrier and why the solution works (or is the bug in the final solution, since the author says he no longer recommends this? (See the ‘this’…pronoun again!).
With that extra time, this would be an excellent resource. As it is, I can only thank the author for trying.
Can you explain how different this is from using a synclock to do the same?