William's profileSo long, and thanks for ...BlogLists Tools Help
    February 10

    EventWaitHandle considered harmful

    Ran across this MSDN page while searching for something else today. A “Community Content” response by “mike_msdn” asked the following question:

    If NewItemEvent fires twice or more while the consumer thread is busy (i.e. not wating on WaitHandle.WaitAny), then won’t the consumer thread only be called once, missing the other calls?

    The short answer to this is “yes”. Like many other code samples in MSDN, this one is utter crap. Just riddled with bugs. Be careful you understand the code when using any examples you find on the Internet, even if the source is one that should be authoritative.

    (An interesting aside: there are folks at Microsoft who understand this topic, yet we still get broken samples in the MSDN.)

    Like the title of this blog says, I’m going to go out on a limb and say that EventWaitHandle should be considered harmful. Actually, this isn’t the first time I went out on that limb, as when I worked on Boost.Threads I constantly had to explain how Win32 synchronization event objects were dangerous little buggers that should be avoided like the plague. I wish the things didn’t exist, as 9 times out of 10 (or worse) when someone uses an EventWaitHandle, they shouldn’t have. It’s a rare scenario in which an EventWaitHandle can be safely used, and an even rarer scenario in which it’s the solution you should choose.

    So, what’s wrong with EventWaitHandle? It doesn’t address the issue of synchronizing access to shared state. This means that if shared state is involved you have to use some other synchronization mechanism in conjunction with the EventWaitHandle. However, this introduces race conditions between the wait and the lock. This is a subtle race condition as well… one that will go undetected for years, and then fail miserably at the worst possible time. Don’t believe me? Do a Google search on how to implement a “condition variable” on Win32. A condition variable is a special synchronization concept that combines the “unlock-wait-lock” set of operations into a single atomic operation, avoiding the race conditions I’m talking about here. When you do the Google search, the first thing you should notice is how complicated many of the solutions are. That should be enough to convince you that EventWaitHandle is the wrong solution in these scenarios. If not, really start to dig into the search results and see how most of those implementations have been proven to be broken. Then look at the implementation in Boost.Threads. If you can understand that implementation, then you have enough knowledge to safely use an EventWaitHandle… but you’ll also know better than to do so ;).

    Back to the question by “mike_msdn". If the sample code is broken, how would you implement a producer consumer? Wikipedia has an entry on this. You could decide to use a Semaphore solution, as the article shows. This requires two Semaphore objects and a lock. Unlike EventWaitHandle solutions, when coded correctly, there’s no race condition between the Wait on the Semaphore and the lock, because we take advantage of the semantics of the Semaphore count. The other solution, and the simplest solution, is to use a “monitor”. Remember that “condition variable” I talked about before? Well, a “monitor” basically marries a “condition variable” and a “mutex” into a single concept. The .NET runtime has supported this concept since the beginning, with the Monitor static class. Here’s partial code to “fix” the buggy MSDN code, using a monitor (you should be able to figure out the missing pieces of code… I don’t have the time right now to create a fully working sample).

    public class SyncObject
    {
        public bool exit;
    }
    
    public class Producer
    {
        private readonly Queue<int> _queue;
        private readonly SyncObject _sync;
        public Producer(Queue<int> q, SyncObject sync)
        {
            _queue = q;
            _sync = sync;
        }
        public void ThreadRun()
        {
            int count = 0;
            Random r = new Random();
            lock (_sync)
            {
                while (true)
                {
                    while (_queue.Count >= 20)
                    { // This loop waits for the consumer
                        Monitor.Wait(_sync);
                    }
                    Monitor.Wait(_sync, 0); // Release the lock to allow exit flag to be set
                    if (_sync.exit)
                    {
                        break;
                    }
                    _queue.Enqueue(r.Next(0, 100));
                    Monitor.Pulse(_sync);
                    count++;
                }
            }
            Console.WriteLine("Producer thread: produced {0} items", count);
        }
    }
    
    public class Consumer
    {
        private readonly Queue<int> _queue;
        private readonly SyncObject _sync;
        public Consumer(Queue<int> q, SyncObject sync)
        {
            _queue = q;
            _sync = sync;
        }
        public void ThreadRun()
        {
            int count = 0;
            lock (_sync)
            {
                while (true)
                {
                    while (_queue.Count == 0 && !_sync.exit)
                    {
                        Monitor.Wait(_sync);
                    }
                    if (_sync.exit)
                    {
                        break;
                    }
                    _queue.Dequeue();
                    Monitor.Pulse(_sync);
                    count++;
                }
            }
            Console.WriteLine("Consumer thread: consumed {0} items", count);
        }
    }
    

    Like I said, the Monitor stuff has been in .NET from the very beginning. It’s sad that very little code makes use of it. It’s scary that a lot of code that doesn’t is instead relying on buggy constructs, often using EventWaitHandle objects.

    Comments (3)

    Please wait...
    Sorry, the comment you entered is too long. Please shorten it.
    You didn't enter anything. Please try again.
    Sorry, we can't add your comment right now. Please try again later.
    To add a comment, you need permission from your parent. Ask for permission
    Your parent has turned off comments.
    Sorry, we can't delete your comment right now. Please try again later.
    You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
    Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
    Complete the security check below to finish leaving your comment.
    The characters you type in the security check must match the characters in the picture or audio.

    To add a comment, sign in with your Windows Live ID (if you use Hotmail, Messenger, or Xbox LIVE, you have a Windows Live ID). Sign in


    Don't have a Windows Live ID? Sign up

    No namewrote:

    http://www.batteryfast.com/acer/as07b31.htm acer as07b31 battery
    http://www.batteryfast.com/laptop-ac-adapter/toshiba/toshiba-15V-5A-75w-6.3mm-3.0mm.php
    http://www.batteryfast.com/laptop-ac-adapter/toshiba/toshiba-15V-6A-90w-6.3mm-3.0mm.php
    http://www.batteryfast.com/laptop-ac-adapter/toshiba/toshiba-19V-3.42A-65w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/toshiba/toshiba-19V-4.74A-90w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/samsung/samsung-19V-3.16A-60w-5.5mm-3.4mm-pin-inside.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-3.95A-75w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-4.74A-90w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-6.3A-120w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-6.3A-120w-4pin-round.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-3.42A-65w-5.5mm-2.5mm-white.php
    http://www.batteryfast.com/laptop-ac-adapter/toshiba/toshiba-15V-8A-120w-special-4-hole.php
    http://www.batteryfast.com/laptop-ac-adapter/liteon/liteon-19V-4.74A-90w-5.5mm-1.7mm.php
    http://www.batteryfast.com/laptop-ac-adapter/hp/hp-19V-4.74A-90w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/hp/hp-18.5V-3.5A-65w-4.8mm-1.7mm.php
    http://www.batteryfast.com/laptop-ac-adapter/hp/hp-19V-4.74A-90w-4.8mm-1.7mm.php
    http://www.batteryfast.com/laptop-ac-adapter/acer/acer-19V-3.42A-65w-5.5mm-2.5mm-big-head.php
    http://www.batteryfast.com/laptop-ac-adapter/acer/acer-19V-3.42A-65w-5.5mm-1.5mm-small-head.php
    http://www.batteryfast.com/laptop-ac-adapter/fujitsu/fujitsu-19V-4.74A-90w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/dell/dell-19V-3.16A-60w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/dell/dell-20V-3.5A-70w-horseshoe-style-special-for-dell-pa-6.php
    http://www.batteryfast.com/laptop-ac-adapter/compaq/compaq-19V-4.74A-90w-5.5mm-2.5mm.php
    http://www.batteryfast.com/laptop-ac-adapter/dell/dell-19.5V-3.34A-65w-7.4mm-5.0mm-with-pin-pa-12.php
    http://www.batteryfast.com/laptop-ac-adapter/asus/asus-19v-3.42A-65w-5.5mm-2.5mm.php
    http://www.batteryfast.com/dell/latitude-d620.htm dell latitude d620 battery
    http://www.batteryfast.com/acer/as07b41.htm acer as07b41 battery
    http://www.batteryfast.com/dell/xps-1330.htm dell xps 1330 battery
    http://www.batteryfast.com/dell/xps-m1330.htm dell xps m1330 battery
    http://www.batteryfast.com/acer/as07b42.htm acer as07b42 battery
    http://www.batteryfast.com/acer/as07b72.htm acer as07b72 battery
    http://www.batteryfast.com/acer/as07b52.htm acer as07b52 battery
    http://www.batteryfast.com/acer/travelmate-4200.htm acer travelmate 4200 battery
    http://www.batteryfast.com/acer/aspire-5100.htm acer aspire 5100 battery
    5 days ago
    I don't follow the logic. If the producer has filled the queue, the consumer isn't going to busy wait, as there's something to consume. I see nothing wrong with this implementation. BTW, the Java synchronized keyword is no different.
    July 20
    No namewrote:
    this appears to be a solution that gets into busy waiting. suppose the producer thread fills up the queue and the consumer thread executes after a long time. then Monitor.Wait(_sync) is essentially busy waiting, since it will repeatedly release and grab lock. i haven't see a solution in C# that uses the monitor abstraction as defined in galvin/silberschatz OS book and as implemented by the synchronized keyword in Java.
    July 17

    Trackbacks

    The trackback URL for this entry is:
    http://wekempf.spaces.live.com/blog/cns!D18C3EC06EA971CF!672.trak
    Weblogs that reference this entry
    • None