William 的个人资料So long, and thanks for ...日志列表 工具 帮助
2月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.

评论 (3)

请稍候...
很抱歉,您输入的评论太长。请缩短您的评论。
您没有输入任何内容,请重试。
很抱歉,我们当前无法添加您的评论。请稍后重试。
若要添加评论,需要您的家长授予您相应权限。请求权限
您的家长禁用了评论功能。
很抱歉,我们当前无法删除您的评论。请稍后重试。
您已超过了一天之内允许提供的评论数上限。请在 24 小时后重试。
因为我们的系统表明您可能在向其他用户提供垃圾评论,您的帐户已禁用了评论功能。如果您认为我们错误地禁用了您的帐户,请联系 Windows Live 支持部门
完成下面的安全检查,您提供评论的过程才能完成。
您在安全检查中键入的字符必须与图片或音频中的字符一致。

若要添加评论,请使用您的 Windows Live ID 登录(如果您使用过 Hotmail、Messenger 或 Xbox LIVE,您就拥有 Windows Live ID)。登录


还没有 Windows Live ID 吗?请注册

没有名字发表:

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
6 天以前
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.
7 月 20 日
没有名字发表:
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.
7 月 17 日

引用通告

此日志的引用通告 URL 是:
http://wekempf.spaces.live.com/blog/cns!D18C3EC06EA971CF!672.trak
引用此项的网络日志