10

Are condition variables & monitors used in C#?

Can someone give me an example?

Tony The Lion
  • 61,704
  • 67
  • 242
  • 415
  • 2
    As well as the lock-statement and the Monitor class, have a look at WaitHandles (http://msdn.microsoft.com/en-us/library/system.threading.waithandle.aspx) which can be very useful and save you from boring boilerplate. – Skurmedel Dec 31 '09 at 16:47

5 Answers5

14

The equivalent of a condition variable that you use just for signaling in .NET is the abstract WaitHandle class. Practical implementations of it are the ManualResetEvent and AutoResetEvent classes.

A condition variable that you use as a monitor requires System.Threading.Monitor. The C# lock statement makes it very easy to use, it ensures the monitor is always exited without explicitly programming the Exit() call.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 2
    Note that exiting a monitor if code throws an exception is not necessarily a good thing; the monitor was probably protecting a mutation to ensure that the result upon exiting the monitor was consistent; an exception is evidence that the mutation was only partially completed and therefore you've just unlocked access to inconsistent state. If the exception is caught and the program continues then you cannot rely upon the program state being consistent. – Eric Lippert Dec 31 '09 at 17:56
  • Very good point, I re-worded that. Thanks. – Hans Passant Dec 31 '09 at 19:23
  • 5
    Wait (pardon the pun). Are any of those actually a direct equivalent of a condition variable?? To my non-expert eye they're nothing like a condition variable. In fact I've seen a webpage that shows how to build a condition variable from windows kernel objects like auto-reset events, and it's a pretty complex process involving more than one such kernel object... – mackenir Mar 16 '12 at 15:20
  • 3
    I agree with @mackenir -- Saying a `WaitHandle` is the same as a condition variable leads to misunderstandings like http://stackoverflow.com/questions/18276944/do-wait-handles-in-net-release-locks-that-a-thread-acquires – Ben Voigt Aug 17 '13 at 01:15
  • @BenVoigt: while **WaitHandle** is not the exact representation of a **condition-variable** it can be used to obtain the same semantics (check+lock). The issue you point to is caused by the misunderstanding of the functioning of WaitHandles. IMHO all the locking scenarios handled by a condition-variable could be implemented with a WaitHandle. I'm probably wrong so if you have a code sample that demonstrates the opposite please share. :) – Pragmateek Nov 02 '13 at 20:08
  • @Pragmateek: Well, you can use WaitHandles for synchronization. But their usage looks nothing like the usage of a condition variable (which unlocks a mutex while waiting on the condition). So their "equivalence" exists at roughly the same level as the equivalence of Turing-complete languages. – Ben Voigt Nov 02 '13 at 20:48
  • @BenVoigt: WaitHandle offers the main feature of a condition-variable, signaling, that avoids spinning threads. The second part, locking, can be managed with a simple mutex. I naively imagine something like: `waithHandle.Wait();mutex.WaitOne();try{...}finally{mutex.ReleaseMutex();}`... – Pragmateek Nov 02 '13 at 21:43
  • @Pragmateek: And now you have deadlocks, because you didn't wait for the whole set of handles atomically. You're familiar with the Dining Philosophers Problem? Even if you use lock sorting, you might still lose events because of the lack of atomicity. – Ben Voigt Nov 02 '13 at 21:51
  • @BenVoigt: any code that uses locks can deadlock if not well designed. Again I'm probably too naive but the above sample does not seem more deadlocks prone than the standard condition-variable use with Pthreads. The WaitHandle can release one or many threads; in the second case only one at a time will be able to enter the critical region. But I'd be glad to be proven wrong, so that I'd learn something. :) – Pragmateek Nov 02 '13 at 22:13
  • @Pragmateek: The problem is if code waits on the mutex first, and then waits on the wait handle. The code that's supposed to set the wait handle probably can't do so until it gets the mutex. Real condition variables solve this by releasing the mutex for exactly the time spent waiting. – Ben Voigt Nov 02 '13 at 22:21
  • @BenVoigt: Ah OK :) With WaitHandle we don't need to acquire the mutex before because it is itself a point of synchronization. So should be fine... – Pragmateek Nov 02 '13 at 22:40
  • 1
    @Pragmateek: Unless you needed the mutex before for some other reason, because maybe you're making changes to the shared data before waiting for the condition to become true. A condition variable is like a cross-thread call. You get the mutex, set the input parameters, wake the other thread (which waits for the mutex), then use a condition variable to atomically unblock the other thread and wait for the result. After that you again hold the mutex so you can read the output parameters. – Ben Voigt Nov 02 '13 at 22:44
  • Sure, there are things that can be done easily with either a condition variable or a `WaitHandle`. But a condition variable is far more powerful. – Ben Voigt Nov 02 '13 at 22:45
  • @BenVoigt: I agree, it's why **Monitor** is probably our best friend in this kind of scenarios. :) – Pragmateek Nov 02 '13 at 22:58
4

System.Threading.Monitor is one way (example within)

jspcal
  • 50,847
  • 7
  • 72
  • 76
3

You can use the Lock object which acts as syntactic sugar for the Monitor class.

lock(someObject)
{
    // Thread safe code here.
}

http://msdn.microsoft.com/en-us/library/c5kehkcz%28VS.80%29.aspx

Brett Allen
  • 5,297
  • 5
  • 32
  • 62
2

As an alternative to ManualResetEvent and friends, Windows now provides native support for condition variables. I haven't benchmarked it myself, but there's a good chance your performance will improve when leveraging the native API.

Here's a Code Project article that explains how to access this (relatively new) construct from C#:

A .NET Wrapper for the Vista/Server 2008 Condition Variable

kgriffs
  • 4,080
  • 5
  • 37
  • 42
1

This version atomically unlocks a Mutex or ReaderWriterLockSlim while waiting for signalling, and relocks it before returning - which is the posix way.

using System.Collections.Concurrent;

namespace System.Threading.More {
    public class ConditionVariable {
        private readonly ConcurrentQueue<ManualResetEventSlim> _waitingThreads = new ConcurrentQueue<ManualResetEventSlim>();

        /// <summary>
        ///     Atomically unlocks and waits for a signal.
        ///     Then relocks the mutex before returning
        /// </summary>
        /// <param name="mutex"></param>
        public void Wait(Mutex mutex) {
            if (mutex == null) {
                throw new ArgumentNullException("mutex");
            }
            var waitHandle = new ManualResetEventSlim();
            try {
                _waitingThreads.Enqueue(waitHandle);
                mutex.ReleaseMutex();
                waitHandle.Wait();
            } finally {
                waitHandle.Dispose();
            }
            mutex.WaitOne();
        }

        public void WaitRead(ReaderWriterLockSlim readerWriterLock) {
            if (readerWriterLock == null) {
                throw new ArgumentNullException("readerWriterLock");
            }
            var waitHandle = new ManualResetEventSlim();
            try {
                _waitingThreads.Enqueue(waitHandle);
                readerWriterLock.ExitReadLock();
                waitHandle.Wait();
            } finally {
                waitHandle.Dispose();
            }
            readerWriterLock.EnterReadLock();
        }

        public void Signal() {
            ManualResetEventSlim waitHandle;
            if (_waitingThreads.TryDequeue(out waitHandle)) {
                waitHandle.Set();
            }
        }

        public void Broadcast() {
            ManualResetEventSlim waitHandle;
            while (_waitingThreads.TryDequeue(out waitHandle)) {
                waitHandle.Set();
            }
        }
    }
}
Brent
  • 4,153
  • 4
  • 30
  • 63