Is a .Net Lock(context) atomic? - c#

I do understand that a .net lock ensures that only a single thread performs the lines of code found within the scope of the lock.
What I don't understand is whether a lock is atomic.
Can a thread be interrupted while performing the locked code?
For example - It appears to me that if a lock is NOT atomic, then the following code is not thread safe:
Class example
{
private int myNumber;
private object context = new object();
void Write()
{
myNumber--;
}
void WriteLock()
{
lock (context)
{
myNumber++;
print(myNumber);
}
}
}
If thread A peforms method WriteLock() and is interrupted because thread B is performing Write(), then myNumber may be changed unsafely. Am I right?

No, that's quacks loudly like a bug. Those operators are not atomic, even though they look like it. Under the hood, they operate as a read-modify-write, three operations instead of one. So are themselves not atomic. The missing lock in Write() permits it to execute concurrently with WriteLock(). The outcome is arbitrary, including no change when Write races ahead of WriteLock and the value actually getting decremented when WriteLock races ahead of Write.
Having a thread interrupted while it owns the lock doesn't matter, the lock just will be held longer.
Use Interlocked.Increment() and Decrement() for a cheaper version that doesn't need lock.

Check the msdn documentation. I would imagine that it is either atomic or is implementing some other pattern to ensure this cannot happen.
The issue with your example is that the Write method should also be obtaining a lock before decrementing myNumber so that no other thread can alter the shared resource.
void Write()
{
lock(context) {
myNumber--;
}
}

Related

Conditional thread lock in c#

Is it possible to have a conditional thread lock when the underlying condition is not constant?
I have two functions A and B, and a condition to decide which function to execute.
A is thread safe by itself, multiple calls to A can execute simultaneously, B is not, and is Synchronized. But during execution of B the condition can change (from false to true) and therefore all threads executing A at that time will throw errors.
if (condition)
{
A();
}
else
{
B();
}
A - thread safe
B - Synchronized using [MethodImpl(MethodImplOptions.Synchronized)]
Therefore, I am looking for a way to lock A but only when B is running.
Please suggest a way to achieve this.
Some elaborations:
I am creating a cache, and performance is very crucial, thus a blanket lock is not feasible.
Condition is whether or not the requested data is present in the cache.
A() = AddToUpdates() - Executed on a cache hit, just adds to the number of updates for a particular cache key, using a concurrent dictionary.
B() = ProccessUpdates() and EvictLeastPriorityEntry() - Executed on a cache miss, all previous updates will be processed and the underlying data structure storing the ordering of cache entries will be re-arranged.
And then the entry with least priority will be removed.
As mentioned in the accepted answer ReaderWriterLock seems to be the way to go.
Just one problem though,
Let's say, thread1 starts execution and a cache hit occurs, (on the entry with the least priority) meaning the if condition is true and enters the if block. But before calling A(), control is switched to thread2.
thread2 - cache miss occurs, reordering and eviction (Entry which A() from thread1 needed access to) is performed.
Now when controlled is returned to thread1, error will occur.
This is the solution I feel should work:
_lock.EnterReadLock();
if (condition)
{
A();
}
_lock.ExitReadLock();
if (!condition)
{
B();
}
void A()
{
// ....
}
void B()
{
_lock.EnterWriteLock();
// ...
_lock.ExitWriteLock();
}
Will this work?
Thank you.
I possible solution to your problem might be the ReaderWriterLockSlim class. This is a synchronization primitive that allows multiple concurrent readers, or one exclusive writer, but not both of those at the same time.
Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.
Example:
private readonly ReaderWriterLockSlim _lock = new();
void A()
{
_lock.EnterReadLock();
try
{
//...
}
finally { _lock.ExitReadLock(); }
}
void B()
{
_lock.EnterWriteLock();
try
{
//...
}
finally { _lock.ExitWriteLock(); }
}
Your question looks a lot like this:
A() is some read only method, so thread safe. Different execution of A in parallel is OK.
B() is like writing/mutating things that A method uses. So A() becomes not thread safe if executed at same time.
For example B() could write in a List and A() executions read on this list. And you would get exception "InvalidOperationException: Collection Was Modified" thrown from A() .
I advise you to look for "producer/consumer problem" in google and look for the tons of example there are.
But in case you absolutely want to begins B execution while A execution(s) has/have not terminated, you can add checkpoint in A() using Monitor class, it is used to lock a resource and synchronize with other threads. It is more complex though and i would go first for producer/consumer pattern to see if it fill the needs
Some more things:
I would check is the use of BlockingCollection<T> class that may fit your exact need too (and is easy to use)
The use of MethodImplOptions.Synchronized is not recommended because it use public lock. We use usually use private lock (object readonly _lock = new object();) so no one except the maintainer of this object can lock on it, thus preventing dead lock (and preventing other people accusing your code of a bug because other people locked your instance of class without knowing you do the same internally)

Is it possible to create a deadlock in C# if nothing but the lock keyword is used around primitive data access?

I've written a lot of multi-threaded C# code, and I've never had a deadlock in any code I've released.
I use the following rules of thumb:
I tend to use nothing but the lock keyword (I also use other techniques such as reader/writer locks, but sparingly, and only if required for speed).
I use Interlocked.Increment if I am dealing with a long.
I tend to use the smallest granular unit of locking: I only tend to lock around primitive data structures such as long, dictionary or list.
I'm wondering if it's even possible to generate a deadlock if these rules are thumb are consistently followed, and if so, what the code would look like?
Update
I also use these rules of thumb:
Avoid adding a lock around anything that could pause indefinitely, especially I/O operations. If you absolutely have to do so, ensure that absolutely everything within the lock will time out after a set TimeSpan.
The objects I use for locking are always dedicated objects, e.g. object _lockDict = new object(); then lock(_lockDict) { // Access dictionary here }.
Update
Great answer from Jon Skeet. It also confirms why I never get deadlocks as I tend to instinctively avoid nested locks, and even if I do use them, I've always instinctively kept the entry order consistent.
And in response to my comment on tending to use nothing but the lock keyword, i.e. using Dictionary + lock instead of ConcurrentDictionary, Jon Skeet made this comment:
#Contango: That's exactly the approach I'd take too.
I'd go for simple code with locking over "clever" lock-free code every time, until there's evidence that it's causing an issue.
Yes, it's easy to deadlock, without actually accessing any data:
private readonly object lock1 = new object();
private readonly object lock2 = new object();
public void Method1()
{
lock(lock1)
{
Thread.Sleep(1000);
lock(lock2)
{
}
}
}
public void Method2()
{
lock(lock2)
{
Thread.Sleep(1000);
lock(lock1)
{
}
}
}
Call both Method1 and Method2 at roughly the same time, and boom - deadlock. Each thread will be waiting for the "inner" lock, which the other thread has acquired as its "outer" lock.
If you make sure you always acquire locks in the same order (e.g. "never acquire lock2 unless you already own lock1) and release the locks in the reverse order (which is implicit if you're acquiring/releasing with lock) then you won't get that sort of deadlock.
You can still get a deadlock with async code, with just a single thread involved - but that involves Task as well:
public async Task FooAsync()
{
BarAsync().Wait(); // Don't do this!
}
public async Task BarAsync()
{
await Task.Delay(1000);
}
If you run that code from a WinForms thread, you'll deadlock in a single thread - FooAsync will be blocking on the task returned by BarAsync, and the continuation for BarAsync won't be able to run because it's waiting to get back onto the UI thread. Basically, you shouldn't issue blocking calls from the UI thread...
As long as you ever only lock on one thing it's impossible, if one thread tries to lock on multiple locks, then yes. The dining philosophers problem nicely illustrates a simple deadlock caused with simple data.
As the other answers have already shown;
void Thread1Method()
{
lock (lock1)
{
// Do smth
lock (lock2)
{ }
}
}
void Thread2Method()
{
lock (lock2)
{
// Do smth
lock (lock2)
{ }
}
}
Addendum to what Skeet wrote:
The problem normally isn't with "only" two locks... (clearly there could be even with only two locks, but we want to play in Hard mode :-) )...
Let's say that in your program there are 10 lockable resources... Let's call them a1...a10. You must be sure that you'll always lock those in the same order, even for subsets of them... If a method needs a3, a5 and a7, and another methods needs a4, a5, a7, you must be sure that both will try locking them in the "right" order. For simplicity sake in this case the order is clear: a1->a10.
Normally lock objects aren't numbered, and/or they aren't taken in a single method... For example:
void MethodA()
{
lock (Lock1)
{
CommonMethod();
}
}
void MethodB()
{
lock (Lock3)
{
CommonMethod();
}
}
void CommonMethod()
{
lock (Lock2)
{
}
}
void MethodC()
{
lock (Lock1)
{
lock (Lock2)
{
lock (Lock3)
{
}
}
}
}
Here, even with the Lock* numbered, it isn't immediately clear that the locks could be taken in the wrong order (MethodB+CommonMethod take Lock3+Lock2, while MethodC takes Lock1+Lock2+Lock3)... It isn't immediately clear and we are playing with three very big advantages: we are speaking of deadlock, so we are looking for them, the locks are numbered and the whole code is around 30 lines.

Volatile fields: How can I actually get the latest written value to a field?

Considering the following example:
private int sharedState = 0;
private void FirstThread() {
Volatile.Write(ref sharedState, 1);
}
private void SecondThread() {
int sharedStateSnapshot = Volatile.Read(ref sharedState);
Console.WriteLine(sharedStateSnapshot);
}
Until recently, I was under the impression that, as long as FirstThread() really did execute before SecondThread(), this program could not output anything but 1.
However, my understanding now is that:
Volatile.Write() emits a release fence. This means no preceding load or store (in program order) may happen after the assignment of 1 to sharedState.
Volatile.Read() emits an acquire fence. This means no subsequent load or store (in program order) may happen before the copying of sharedState to sharedStateSnapshot.
Or, to put it another way:
When sharedState is actually released to all processor cores, everything preceding that write will also be released, and,
When the value in the address sharedStateSnapshot is acquired; sharedState must have been already acquired.
If my understanding is therefore correct, then there is nothing to prevent the acquisition of sharedState being 'stale', if the write in FirstThread() has not already been released.
If this is true, how can we actually ensure (assuming the weakest processor memory model, such as ARM or Alpha), that the program will always print 1? (Or have I made an error in my mental model somewhere?)
Your understanding is correct, and it is true that you cannot ensure that the program will always print 1 using these techniques. To ensure your program will print 1, assuming thread 2 runs after thread one, you need two fences on each thread.
The easiest way to achieve that is using the lock keyword:
private int sharedState = 0;
private readonly object locker = new object();
private void FirstThread()
{
lock (locker)
{
sharedState = 1;
}
}
private void SecondThread()
{
int sharedStateSnapshot;
lock (locker)
{
sharedStateSnapshot = sharedState;
}
Console.WriteLine(sharedStateSnapshot);
}
I'd like to quote Eric Lippert:
Frankly, I discourage you from ever making a volatile field. Volatile fields are a sign that you are doing something downright crazy: you're attempting to read and write the same value on two different threads without putting a lock in place.
The same applies to calling Volatile.Read and Volatile.Write. In fact, they are even worse than volatile fields, since they require you to do manually what the volatile modifier does automatically.
You're right, there's no guarantee that release stores will be immediately visible to all processors. Volatile.Read and Volatile.Write give you acquire/release semantics, but no immediacy guarantees.
The volatile modifier seems to do this though. The compiler will emit an OpCodes.Volatile IL instruction, and the jitter will tell the processor not to store the variable on any of its registers (see Hans Passant's answer).
But why do you need it to be immediate anyway? What if your SecondThread happens to run a couple of milliseconds sooner, before the values are actually wrote? Seeing as the scheduling is non-deterministic, the correctness of your program shouldn't depend on this "immediacy" anyway.
Until recently, I was under the impression that, as long as
FirstThread() really did execute before SecondThread(), this program
could not output anything but 1.
As you go on to explain yourself, this impression is wrong. Volatile.Read simply issues a read operation on its target followed by a memory barrier; the memory barrier prevents operation reordering on the processor executing the current thread but this does not help here because
There are no operations to reorder (just the single read or write in each thread).
The race condition across your threads means that even if the no-reorder guarantee applied across processors, it would simply mean that the order of operations which you cannot predict anyway would be preserved.
If my understanding is therefore correct, then there is nothing to
prevent the acquisition of sharedState being 'stale', if the write in
FirstThread() has not already been released.
That is correct. In essence you are using a tool designed to help with weak memory models against a possible problem caused by a race condition. The tool won't help you because that's not what it does.
If this is true, how can we actually ensure (assuming the weakest
processor memory model, such as ARM or Alpha), that the program will
always print 1? (Or have I made an error in my mental model
somewhere?)
To stress once again: the memory model is not the problem here. To ensure that your program will always print 1 you need to do two things:
Provide explicit thread synchronization that guarantees the write will happen before the read (in the simplest case, SecondThread can use a spin lock on a flag which FirstThread uses to signal it's done).
Ensure that SecondThread will not read a stale value. You can do this trivially by marking sharedState as volatile -- while this keyword has deservedly gotten much flak, it was designed explicitly for such use cases.
So in the simplest case you could for example have:
private volatile int sharedState = 0;
private volatile bool spinLock = false;
private void FirstThread()
{
sharedState = 1;
// ensure lock is released after the shared state write!
Volatile.Write(ref spinLock, true);
}
private void SecondThread()
{
SpinWait.SpinUntil(() => spinLock);
Console.WriteLine(sharedState);
}
Assuming no other writes to the two fields, this program is guaranteed to output nothing other than 1.

Why does this code not end in a deadlock

I have this C# code:
public class Locking
{
private int Value1; private int Value2;
private object lockValue = new Object();
public int GetInt1(int value1, int value2)
{
lock (lockValue)
{
Value1 = value1;
Value2 = value2;
return GetResult();
}
}
public int GetInt2(int value1, int value2)
{
lock (lockValue)
{
return GetInt1(value1, value2);
}
}
private int GetResult()
{
return Value1 + Value2;
}
}
So basically I expect a deadlock if I execute GetInt2 but the code just executes. Any good explanation.
The lock blocks the executing thread, unless that thread already holds the lock on the object.
In this case, there is only one thread executing; it takes the lock on lockValue in GetInt2, then proceeds into GetInt1, where it encounters a lock statement on lockValue again - which it already holds, so it is allowed to proceed.
The lock statement in C# is syntactic sugar, interpreted by the compiler as a call to Monitor.Enter. It's documented (in the "Monitors" section) that
lock (x)
{
DoSomething();
}
is equivalent to
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
DoSomething();
}
finally
{
System.Threading.Monitor.Exit(obj);
}
The documentation for Monitor.Enter states that
It is legal for the same thread to invoke Enter more than once
without it blocking; however, an equal number of Exit calls must be
invoked before other threads waiting on the object will unblock.
It's obvious from the above that the given code will produce no deadlock as long as only one thread is involved.
The general case here is whether or not a synchronization object is re-entrant. In other words, can be acquired again by the same thread if it already owns the lock. Another way to say that is whether the object has "thread affinity".
In .NET, the Monitor class (which implements the lock statement), Mutex and ReaderWriterLock are re-entrant. The Semaphore and SemaphoreSlim classes are not, you could get your code to deadlock with a binary semaphore. The cheapest way to implement locking is with Interlocked.CompareExchange(), it would also not be re-entrant.
There is an extra cost associated with making a sync object re-entrant, it needs to keep track of which thread owns it and how often the lock was acquired on the owning thread. Which requires storing Thread.ManagedId and a counter, two ints. This affected choices in C++ for example, the C++11 language specification finally adding threading to the standard library. The std::mutex class is not re-entrant in that language and proposals to add a recursive version were rejected. They considered the overhead of making it re-entrant too high. A bit heavy-handed perhaps, a cost that's rather miniscule against the amount of time spent on debugging accidental deadlock :) But it is a language where it is no slamdunk that acquiring the thread ID can be guaranteed to be cheap like it is in .NET.
This is exposed in the ReaderWriterLockSlim class, you get to choose. Note the RecursionPolicy property, allowing you to choose between NoRecursion and SupportsRecursion. The NoRecursion mode is cheaper and makes it truly slim.

C# manual lock/unlock

I have a function in C# that can be called multiple times from multiple threads and I want it to be done only once so I thought about this:
class MyClass
{
bool done = false;
public void DoSomething()
{
lock(this)
if(!done)
{
done = true;
_DoSomething();
}
}
}
The problem is _DoSomething takes a long time and I don't want many threads to wait on it when they can just see that done is true.
Something like this can be a workaround:
class MyClass
{
bool done = false;
public void DoSomething()
{
bool doIt = false;
lock(this)
if(!done)
doIt = done = true;
if(doIt)
_DoSomething();
}
}
But just doing the locking and unlocking manually will be much better.
How can I manually lock and unlock just like the lock(object) does? I need it to use same interface as lock so that this manual way and lock will block each other (for more complex cases).
The lock keyword is just syntactic sugar for Monitor.Enter and Monitor.Exit:
Monitor.Enter(o);
try
{
//put your code here
}
finally
{
Monitor.Exit(o);
}
is the same as
lock(o)
{
//put your code here
}
Thomas suggests double-checked locking in his answer. This is problematic. First off, you should not use low-lock techniques unless you have demonstrated that you have a real performance problem that is solved by the low-lock technique. Low-lock techniques are insanely difficult to get right.
Second, it is problematic because we don't know what "_DoSomething" does or what consequences of its actions we are going to rely on.
Third, as I pointed out in a comment above, it seems crazy to return that the _DoSomething is "done" when another thread is in fact still in the process of doing it. I don't understand why you have that requirement, and I'm going to assume that it is a mistake. The problems with this pattern still exist even if we set "done" after "_DoSomething" does its thing.
Consider the following:
class MyClass
{
readonly object locker = new object();
bool done = false;
public void DoSomething()
{
if (!done)
{
lock(locker)
{
if(!done)
{
ReallyDoSomething();
done = true;
}
}
}
}
int x;
void ReallyDoSomething()
{
x = 123;
}
void DoIt()
{
DoSomething();
int y = x;
Debug.Assert(y == 123); // Can this fire?
}
Is this threadsafe in all possible implementations of C#? I don't think it is. Remember, non-volatile reads may be moved around in time by the processor cache. The C# language guarantees that volatile reads are consistently ordered with respect to critical execution points like locks, and it guarantees that non-volatile reads are consistent within a single thread of execution, but it does not guarantee that non-volatile reads are consistent in any way across threads of execution.
Let's look at an example.
Suppose there are two threads, Alpha and Bravo. Both call DoIt on a fresh instance of MyClass. What happens?
On thread Bravo, the processor cache happens to do a (non-volatile!) fetch of the memory location for x, which contains zero. "done" happens to be on a different page of memory which is not fetched into the cache quite yet.
On thread Alpha at the "same time" on a different processor DoIt calls DoSomething. Thread Alpha now runs everything in there. When thread Alpha is done its work, done is true and x is 123 on Alpha's processor. Thread Alpha's processor flushes those facts back out to main memory.
Thread bravo now runs DoSomething. It reads the page of main memory containing "done" into the processor cache and sees that it is true.
So now "done" is true, but "x" is still zero in the processor cache for thread Bravo. Thread Bravo is not required to invalidate the portion of the cache that contains "x" being zero because on thread Bravo neither the read of "done" nor the read of "x" were volatile reads.
The proposed version of double-checked locking is not actually double-checked locking at all. When you change the double-checked locking pattern you need to start over again from scratch and re-analyze everything.
The way to make this version of the pattern correct is to make at least the first read of "done" into a volatile read. Then the read of "x" will not be permitted to move "ahead" of the volatile read to "done".
You can check the value of done before and after the lock:
if (!done)
{
lock(this)
{
if(!done)
{
done = true;
_DoSomething();
}
}
}
This way you won't enter the lock if done is true. The second check inside the lock is to cope with race conditions if two threads enter the first if at the same time.
BTW, you shouldn't lock on this, because it can cause deadlocks. Lock on a private field instead (like private readonly object _syncLock = new object())
The lock keyword is just syntactic sugar for the Monitor class. Also you could call Monitor.Enter(), Monitor.Exit().
But the Monitor class itself has also the functions TryEnter() and Wait() which could help in your situation.
I know this answer comes several years late, but none of the current answers seem to address your actual scenario, which only became apparent after your comment:
The other threads don't need to use any information generated by ReallyDoSomething.
If the other threads don't need to wait for the operation to complete, the second code snippet in your question would work fine. You can optimize it further by eliminating your lock entirely and using an atomic operation instead:
private int done = 0;
public void DoSomething()
{
if (Interlocked.Exchange(ref done, 1) == 0) // only evaluates to true ONCE
_DoSomething();
}
Furthermore, if your _DoSomething() is a fire-and-forget operation, then you might not even need the first thread to wait for it, allowing it to run asynchronously in a task on the thread pool:
int done = 0;
public void DoSomething()
{
if (Interlocked.Exchange(ref done, 1) == 0)
Task.Factory.StartNew(_DoSomething);
}

Categories

Resources