6

I have written what I hope is a lightweight alternative to using the ManualResetEvent and AutoResetEvent classes in C#/.NET. The reasoning behind this was to have Event like functionality without the weight of using a kernel locking object.

Although the code seems to work well in both testing and production, getting this kind of thing right for all possibilities can be a fraught undertaking and I would humbly request any constructive comments and or criticism from the StackOverflow crowd on this. Hopefully (after review) this will be useful to others.

Usage should be similar to the Manual/AutoResetEvent classes with Notify() used for Set().

Here goes:

using System;
using System.Threading;

public class Signal
{
  private readonly object _lock = new object();
  private readonly bool _autoResetSignal;
  private bool _notified;

  public Signal()
    : this(false, false)
  {
  }

  public Signal(bool initialState, bool autoReset)
  {
    _autoResetSignal = autoReset;
    _notified = initialState;
  }

  public virtual void Notify()
  {
    lock (_lock)
    {
      // first time?
      if (!_notified)
      {
        // set the flag
        _notified = true;

        // unblock a thread which is waiting on this signal 
        Monitor.Pulse(_lock);
      }
    }
  }

  public void Wait()
  {
    Wait(Timeout.Infinite);
  }

  public virtual bool Wait(int milliseconds)
  {
    lock (_lock)
    {
      bool ret = true;
      // this check needs to be inside the lock otherwise you can get nailed
      // with a race condition where the notify thread sets the flag AFTER 
      // the waiting thread has checked it and acquires the lock and does the 
      // pulse before the Monitor.Wait below - when this happens the caller
      // will wait forever as he "just missed" the only pulse which is ever 
      // going to happen 
      if (!_notified)
      {
        ret = Monitor.Wait(_lock, milliseconds);
      }

      if (_autoResetSignal)
      {
        _notified = false;
      }
      return (ret);
    }
  }
}
tshepang
  • 12,111
  • 21
  • 91
  • 136
sweetlilmre
  • 682
  • 6
  • 13
  • 1
    Have you benchmarked this against using ManualResetEvent/AutoResetEvents? How significant is the performance difference? – James May 12 '10 at 08:04
  • No I haven't as yet as the primary goal was to make a non kernel handle / resource using event object. I'll try to set up some tests though, thanks. – sweetlilmre May 12 '10 at 08:38
  • 2
    Implementing your own threading primitives is like implementing your own crypto algorithms. Unless you're an expert in the domain, you *will* screw it up. Even if you *are* an expert, you still *might* screw it up. Don't do it. .NET 4 already has "lightweight" versions anyway, `ManualResetEventSlim` and related classes. – Aaronaught May 12 '10 at 22:04
  • @Aaronaught: thanks for that I was not aware of ManualResetEventSlim which will be useful. Still I am caught in 3.51 for now... – sweetlilmre May 13 '10 at 10:07
  • I'd also check [this](http://stackoverflow.com/a/1355415/124006) answer to my question about which is best, monitor/pulse or manual reset event. – Matt Apr 08 '13 at 01:56

4 Answers4

4

This works from the assumption that Win32 events are expensive. They are not, there's little that I can think of that's cheaper than an event. A major hint that this is so is the .NET designers deciding that it would be a good idea to use a Win32 event to implement MRE and ARE.

The true cost of your replacement is the major FUD you'll experience when you've got a threading race and don't know what causes it.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 1
    Well the assumption is that ARE and MRE use a kernel event under the hood which would be more expensive than a Monitor. I would love to be proven wrong :) – sweetlilmre May 12 '10 at 14:30
  • This article by microsoft details which one's are fast: http://msdn.microsoft.com/en-us/library/ms228964.aspx – David d C e Freitas Apr 19 '12 at 07:41
  • From the perspective of a kernel-mode transition in .NET, Win32 events are expensive, hence the lightweight additions in .NET 4 as per @Aaronaught answer above. – sweetlilmre Nov 21 '12 at 06:13
2

One way to optimize AutoResetEvent's performance is to keep its state (signaled/non signaled) in your own variable, so before making the trip into the kernel and actually using the event object, you could just check the state of your applicative variable and remain in user-mode the whole time.
I've posted a demonstration of this concept a couple of months ago.

Liran
  • 1,596
  • 1
  • 13
  • 11
1

Unfortunately, correct Monitor implementation is fairly heavyweight given the Win32 synchronization primitives. My initial suspicion is that "lock" would be heavier in resource use than an event (and is likely built on top of an event).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I am pretty sure that lock is in no way implemented by an event under the hood. IIRC there are special thread structures used specifically to handle thread locking and to make this more lightweight than events. – sweetlilmre May 12 '10 at 14:28
  • @sweetlilmre - I looked in the Rotor/SharedCCI implementation, and they do use some spinlock-style locks but it does come down to an event type supplied by the host if necessary. – Stephen Cleary May 12 '10 at 21:41
  • Yes, they spin themselves, using their own syncblock bits. The one thing I haven't grokked is how they let the Windows scheduler know. Got a code loc? – Hans Passant May 12 '10 at 22:03
  • SyncBlock code is in clr/src/vm/syncblk.* (the SyncBlock class contains an AwareLock which does the spinning but passes any blocking off to the CLREvent class). The CLREvent class is in clr/src/vm/synch.*, and can be used to create different kinds of events, most of which just call into the host via IHostSyncManager. – Stephen Cleary May 13 '10 at 14:30
0

Generally any sychronization is slow, so it is the best to avoid them.

However, there is a big difference between their speed:

The CPU supported Interlocked Exchange is the fastest while a simple boolean flag still beats AutoresetEvent.

Check out this for full code samples and performance comparisons on AutoResetEvent and alternatives.

Adam
  • 21
  • 1
  • 4