I have a private collection (like an ISet<int>) that can be accessed or modified by several threads.
I have logic such that this thread only needs to mutate the ISet in certain states, whereas the other thread always needs to mutate it. The other thread will always take a lock, implemented the lock keyword on the ISet like this:
lock (this._set)
{
// some mutating operation on this._set
}
For this thread, I only need to perform an operation that mutates the thread if the ISet contains an int called currentValue, which in this case is rare. The operation in the other thread is frequent. Therefore the question is whether I can check to see if the ISet contains currentValue before taking the lock, to minimize the time that this thread blocks the other thread, like this:
if (this._set.Contains(currentValue))
{
lock (this._set)
{
if (this._set.Contains(currentValue))
{
// some mutating operation on this._set
}
}
}
The question is whether the behavior of the check outside the lock is undefined, or if it can result in the ISet being put into an undefined or unexpected state. Of course the actual value returned cannot be trusted to use, which is why it's checked again inside the lock, but is this a valid optimization that I can expect to work, or will it mess up some internal state of the set? The goal is to take the lock in this thread as rarely as possible.
Just reading the collection values won't throw an exception or change the values of the collection if that's what you mean. It may return dirty data, but you already stated that you expect that possibility. I believe reading from a non-thread safe collection like this can even give you incomplete data, or data that is half old state, half new state (see this response to C# multi-threading: Acquire read lock necessary?
Let's assume the first test for _set.Contains(currentValue) returns true and another thread updates the collection immediately after, removing currentValue. You then enter the lock, but now when you test _set.Contains(currentValue), it returns false.
The question is how critical is it for this thread to mutate the collection? Your code may miss out on mutating the collection every time. Your optimization may end up having the opposite effect - slowing the code down by unnecessarily locking.
It would be difficult to provide you a good alternative without knowing other requirements, but I think I've answered your questions and shown that the code is really not optimized at all, but by itself won't break anything.
Related
Lets just say you have a simple operation that runs on a background thread. You want to provide a way to cancel this operation so you create a boolean flag that you set to true from the click event handler of a cancel button.
private bool _cancelled;
private void CancelButton_Click(Object sender ClickEventArgs e)
{
_cancelled = true;
}
Now you're setting the cancel flag from the GUI thread, but you're reading it from the background thread. Do you need to lock before accessing the bool?
Would you need to do this (and obviously lock in the button click event handler too):
while(operationNotComplete)
{
// Do complex operation
lock(_lockObject)
{
if(_cancelled)
{
break;
}
}
}
Or is it acceptable to do this (with no lock):
while(!_cancelled & operationNotComplete)
{
// Do complex operation
}
Or what about marking the _cancelled variable as volatile. Is that necessary?
[I know there is the BackgroundWorker class with it's inbuilt CancelAsync() method, but I'm interested in the semantics and use of locking and threaded variable access here, not the specific implementation, the code is just an example.]
There seems to be two theories.
1) Because it is a simple inbuilt type (and access to inbuilt types is atomic in .net) and because we are only writing to it in one place and only reading on the background thread there is no need to lock or mark as volatile.
2) You should mark it as volatile because if you don't the compiler may optimise out the read in the while loop because it thinks nothing it capable of modifying the value.
Which is the correct technique? (And why?)
[Edit: There seem to be two clearly defined and opposing schools of thought on this. I am looking for a definitive answer on this so please if possible post your reasons and cite your sources along with your answer.]
Firstly, threading is tricky ;-p
Yes, despite all the rumours to the contrary, it is required to either use lock or volatile (but not both) when accessing a bool from multiple threads.
For simple types and access such as an exit flag (bool), then volatile is sufficient - this ensures that threads don't cache the value in their registers (meaning: one of the threads never sees updates).
For larger values (where atomicity is an issue), or where you want to synchronize a sequence of operations (a typical example being "if not exists and add" dictionary access), a lock is more versatile. This acts as a memory-barrier, so still gives you the thread safety, but provides other features such as pulse/wait. Note that you shouldn't use a lock on a value-type or a string; nor Type or this; the best option is to have your own locking object as a field (readonly object syncLock = new object();) and lock on this.
For an example of how badly it breaks (i.e. looping forever) if you don't synchronize - see here.
To span multiple programs, an OS primitive like a Mutex or *ResetEvent may also be useful, but this is overkill for a single exe.
_cancelled must be volatile. (if you don't choose to lock)
If one thread changes the value of _cancelled, other threads might not see the updated result.
Also, I think the read/write operations of _cancelled are atomic:
Section 12.6.6 of the CLI spec states:
"A conforming CLI shall guarantee that
read and write access to properly
aligned memory locations no larger
than the native word size is atomic
when all the write accesses to a
location are the same size."
Locking is not required because you have a single writer scenario and a boolean field is a simple structure with no risk of corrupting the state (while it was possible to get a boolean value that is neither false nor true). But you have to mark the field as volatile to prevent the compiler from doing some optimizations. Without the volatile modifier the compiler could cache the value in a register during the execution of your loop on your worker thread and in turn the loop would never recognize the changed value. This MSDN article (How to: Create and Terminate Threads (C# Programming Guide)) addresses this issue.
While there is need for locking, a lock will have the same effect as marking the field volatile.
For thread synchronization, it's recommended that you use one of the EventWaitHandle classes, such as ManualResetEvent. While it's marginally simpler to employ a simple boolean flag as you do here (and yes, you'd want to mark it as volatile), IMO it's better to get into the practice of using the threading tools. For your purposes, you'd do something like this...
private System.Threading.ManualResetEvent threadStop;
void StartThread()
{
// do your setup
// instantiate it unset
threadStop = new System.Threading.ManualResetEvent(false);
// start the thread
}
In your thread..
while(!threadStop.WaitOne(0) && !operationComplete)
{
// work
}
Then in the GUI to cancel...
threadStop.Set();
Look up Interlocked.Exchange(). It does a very fast copy into a local variable which can be used for comparison. It is faster than lock().
Suppose you have a property public Foo Bar { get; } that you want to lazily initialize. One such approach might be to use the Interlocked class, which guarantees atomicity for certain sequences of operations (e.g. incrementing, adding, compare-exchange). You could do this:
private Foo _bar;
public Foo Bar
{
get
{
// Initial check, necessary so we don't call new Foo() every time when invoking the method
if (_bar == null)
{
// In the unlikely case 2 threads come in here
// at the same time, new Foo() will simply be called
// twice, but only one of them will be set to _bar
Interlocked.CompareExchange(ref _bar, new Foo(), null);
}
return _bar;
}
}
There are many places that demonstrate this approach to lazy initialization, e.g. this blog and places in the .NET Framework itself.
My question is, shouldn't the read from _bar be volatile? For example Thread 1 could have called CompareExchange, setting the value of _bar, but that change wouldn't be visible to Thread 2 since (if I understand this question correctly) it may have cached the value of _bar as null, and it may end up calling Interlocked.CompareExchange again, despite the fact that Thread 1 already set _bar. So shouldn't _bar be marked volatile, to prevent this from happening?
In short, is this approach or this approach to lazy initialization correct? Why is Volatile.Read (which has the same effect as marking a variable volatile and reading from it) used in one case, but not the other?
edit TL;DR: If one thread updates the value of a field via Interlocked.CompareExchange, will that updated value be immediately visible to other threads performing non-volatile reads of the field?
My first thought is "who cares?" :)
What I mean is this: the double-check initialization pattern is almost always overkill and it's easy to get wrong. Most of the time, a simple lock is best: it's easy to write, performant enough, and clearly expresses the intent of the code. Furthermore, we now have the Lazy<T> class to abstract lazy initialization, further removing any need for us to manually implement code to do it.
So, the specifics of the double-check pattern aren't really all that important, because we shouldn't be using it anyway.
That said, I would agree with your observation that the read ought to be a volatile read. Without that, the memory barrier provided by Interlocked.CompareExchange() won't necessarily help.
This is mitigated by two things though:
The double-check pattern isn't guaranteed anyway. There's a race condition even if you have a volatile read, so it has to be safe to initialize twice. So even if the memory location is stale, nothing truly bad will happen. You'll call the Foo constructor twice, which isn't great but it can't be a fatal problem, because that could happen anyway.
On x86, memory access is volatile by default. So it's really only on other platforms that this becomes an issue anyway.
In this particular case doing non-volatile read won't make the code incorrect because even if the second thread's missed update of _bar it will observe it on CompareExchange. Volatile read (potentially) allows to see the updated value earlier without necessity to do heavyweight CompareExchange.
In other cases a memory location written via Interlocked, Volatile.Write, or inside lock region must be read as volatile.
There are a fair share of questions about Interlocked vs. volatile here on SO, I understand and know the concepts of volatile (no re-ordering, always reading from memory, etc.) and I am aware of how Interlocked works in that it performs an atomic operation.
But my question is this: Assume I have a field that is read from several threads, which is some reference type, say: public Object MyObject;. I know that if I do a compare exchange on it, like this: Interlocked.CompareExchange(ref MyObject, newValue, oldValue) that interlocked guarantees to only write newValue to the memory location that ref MyObject refers to, if ref MyObject and oldValue currently refer to the same object.
But what about reading? Does Interlocked guarantee that any threads reading MyObject after the CompareExchange operation succeeded will instantly get the new value, or do I have to mark MyObject as volatile to ensure this?
The reason I'm wondering is that I've implemented a lock-free linked list which updates the "head" node inside itself constantly when you prepend an element to it, like this:
[System.Diagnostics.DebuggerDisplay("Length={Length}")]
public class LinkedList<T>
{
LList<T>.Cell head;
// ....
public void Prepend(T item)
{
LList<T>.Cell oldHead;
LList<T>.Cell newHead;
do
{
oldHead = head;
newHead = LList<T>.Cons(item, oldHead);
} while (!Object.ReferenceEquals(Interlocked.CompareExchange(ref head, newHead, oldHead), oldHead));
}
// ....
}
Now after Prepend succeeds, are the threads reading head guaranteed to get the latest version, even though it's not marked as volatile?
I have been doing some empirical tests, and it seems to be working fine, and I have searched here on SO but not found a definitive answer (a bunch of different questions and comments/answer in them all say conflicting things).
Does Interlocked guarantee that any threads reading MyObject after the CompareExchange operation succeeded will instantly get the new value, or do I have to mark MyObject as volatile to ensure this?
Yes, subsequent reads on the same thread will get the new value.
Your loop unrolls to this:
oldHead = head;
newHead = ... ;
Interlocked.CompareExchange(ref head, newHead, oldHead) // full fence
oldHead = head; // this read cannot move before the fence
EDIT:
Normal caching can happen on other threads. Consider:
var copy = head;
while ( copy == head )
{
}
If you run that on another thread, the complier can cache the value of head and never see the update.
Your code should work fine. Though it is not clearly documented the Interlocked.CompareExchange method will produce a full-fence barrier. I suppose you could make one small change and omit the Object.ReferenceEquals call in favor of relying on the != operator which would perform reference equality by default.
For what it is worth the documentation for the InterlockedCompareExchange Win API call is much better.
This function generates a full memory barrier (or fence) to ensure
that memory operations are completed in order.
It is a shame the same level documenation does not exist on the .NET BCL counterpart Interlocked.CompareExchange because it is very likely they map to the exact same underlying mechanisms for the CAS.
Now after Prepend succeeds, are the threads reading head guaranteed to
get the latest version, even though it's not marked as volatile?
No, not necessarily. If those threads do not generate an acquire-fence barrier then there is no guarantee that they will read the latest value. Make sure that you perform a volatile read upon any use of head. You have already ensured that in Prepend with the Interlocked.CompareExchange call. Sure, that code may go through the loop once with an stale value of head, but the next iteration will be refreshed due to the Interlocked operation.
So if the context of your question was in regards to other threads who are also executing Prepend then nothing more needs to be done.
But if the context of your question was in regards to other threads executing another method on LinkedList then make sure you use Thread.VolatileRead or Interlocked.CompareExchange where appropriate.
Side note...there may be a micro-optimization that could be performed on the following code.
newHead = LList<T>.Cons(item, oldHead);
The only problem I see with this is that memory is allocated on every iteration of the loop. During periods of high contention the loop may spin around several times before it finally succeeds. You could probably lift this line outside of the loop as long as you reassign the linked reference to oldHead on every iteration (so that you get the fresh read). This way memory is only allocated once.
When implementing a thread safe list or queue; does it require to lock on the List.Count property before returning the Count i.e:
//...
public int Count
{
lock (_syncObject)
{
return _list.Count;
}
}
//...
Is it necessary to do a lock because of the original _list.Count variable maybe not a volatile variable?
Yes, that is necessary, but mostly irrelevant. Without the lock you could be reading stale values or even, depending on the inner working and the type of _list.Count, introduce errors.
But note that using that Count property is problematic, any calling code cannot really rely on it:
if (myStore.Count > 0) // this Count's getter locks internally
{
var item = myStore.Dequeue(); // not safe, myStore could be empty
}
So you should aim for a design where checking the Count and acting on it are combined:
ItemType GetNullOrFirst()
{
lock (_syncObject)
{
if (_list.Count > 0)
{
....
}
}
}
Additional:
is it nessesary to do a lock because of the original _list.Count variable maybe not a volatile variable?
The _list.Count is not a variable but a property. It cannot be marked volatile. Whether it is thread-safe depends on the getter code of the property, but it will usually be safe. But unreliable.
It depends.
Now, theoretically, because the underlying value is not threadsafe, just about anything could go wrong because of something the implementation does. In practice though, it's a read from a variable of 32-bits and so guaranteed to be atomic. Hence it might be stale, but it will be a real stale value rather than garbage caused by reading half a value before a change and the other half after it.
So the question is, does staleness matter? Possibly it doesn't. The more you can put up with staleness the better for performance (because the more you can put up with it the less you need to do to ensure you don't have anything stale). E.g. if you were putting the count out to the UI and the collection was changing rapidly, then just the time it takes for the person to read the number and process it in their own brain is going to be enough to make it obsolete anyway, and hence stale.
If however, you needed it to ensure an operation was reasonable before it was attempted, then that staleness is going to cause problems.
However, that staleness is going to happen anyway, because in between your locked (and guaranteed to be fresh) read and the operation happening, there's the opportunity for the collection to change, so you've got a race condition even when locking.
Hence, if freshness is important, you are going to have to lock at a higher level. With this being the case, the lower-level lock is just a waste. Hence you can probably do without it at that point.
The important thing here is that even with a class that is threadsafe in every regard, the best that can guarantee is that each operation will be fresh (though even that may not be guaranteed; indeed when there are more than one core involved "fresh" begins to become meaningless as changes that are near to truly simultaneous can happen) and that each operation won't put the object into an invalid safe. It is still possible to write non-threadsafe code with threadsafe objects (indeed very many non-threadsafe objects are composed of ints, strings and bools or objects that are in turn composed of them, and each of those is threadsafe in and of itself).
One thing that can be useful with mutable classes intended for multithreaded use are synchronised "Try" methods. E.g. to use a List as a stack we need the following operations:
See if the list is empty, reporting failure if it is.
Obtain the "top" value.
Remove the top value.
Even if we synchronise each of those individually, the code doing so won't be threadsafe as something can happen between each step. However, we can provide a Pop() method that does each three in one synchronised method. Analogous approaches are also useful with lock-free classes (where different techniques are used to ensure the method either succeeds or fails completely, and without damage to the object).
No, you don't need to lock, but the caller should otherwise something like this might happen
count is n
thread asks for count
Count returns n
another thread dequeues
count is n - 1
thread asking for count sees count is n when count is actually n - 1
I have two threads, a producer thread that places objects into a generic List collection and a consumer thread that pulls those objects out of the same generic List. I've got the reads and writes to the collection properly synchronized using the lock keyword, and everything is working fine.
What I want to know is if it is ok to access the Count property without first locking the collection.
JaredPar refers to the Count property in his blog as a decision procedure that can lead to race conditions, like this:
if (list.Count > 0)
{
return list[0];
}
If the list has one item and that item is removed after the Count property is accessed but before the indexer, an exception will occur. I get that.
But would it be ok to use the Count property to, say, determine the initial size a completely different collection? The MSDN documentation says that instance members are not guaranteed to be thread safe, so should I just lock the collection before accessing the Count property?
I suspect it's "safe" in terms of "it's not going to cause anything to go catastrophically wrong" - but that you may get stale data. That's because I suspect it's just held in a simple variable, and that that's likely to be the case in the future. That's not the same as a guarantee though.
Personally I'd keep it simple: if you're accessing shared mutable data, only do so in a lock (using the same lock for the same data). Lock-free programming is all very well if you've got appropriate isolation in place (so you know you've got appropriate memory barriers, and you know that you'll never be modifying it in one thread while you're reading from it in another) but it sounds like that isn't the case here.
The good news is that acquiring an uncontested lock is incredibly cheap - so I'd go for the safe route if I were you. Threading is hard enough without introducing race conditions which are likely to give no significant performance benefit but at the cost of rare and unreproducible bugs.