Condition variables in C# [closed]
Asked Answered
C

5

10

Are condition variables & monitors used in C#?

Can someone give me an example?

Cyclotron answered 31/12, 2009 at 16:37 Comment(1)
As well as the lock-statement and the Monitor class, have a look at WaitHandles (msdn.microsoft.com/en-us/library/…) which can be very useful and save you from boring boilerplate.Disesteem
A
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.

Assurgent answered 31/12, 2009 at 16:50 Comment(13)
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.Hylotheism
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...Carleton
I agree with @Carleton -- Saying a WaitHandle is the same as a condition variable leads to misunderstandings like #18277444Naissant
@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. :)Operator
@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.Naissant
@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();}...Operator
@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.Naissant
@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. :)Operator
@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.Naissant
@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...Operator
@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.Naissant
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.Naissant
@BenVoigt: I agree, it's why Monitor is probably our best friend in this kind of scenarios. :)Operator
D
4

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

Dolce answered 31/12, 2009 at 16:41 Comment(0)
C
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

Coastline answered 31/12, 2009 at 16:39 Comment(2)
One small correction, it is not an object, but a keyword, and is spelled lowercase, lock :)Disesteem
Thanks, been developing in VB.NET at work, so had SyncLock on the brain and just removed the Sync part heh.Coastline
C
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

Cuthbertson answered 8/2, 2011 at 20:18 Comment(0)
W
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();
            }
        }
    }
}
Woeful answered 1/11, 2014 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.