I went static mostly as a matter of style. I prefer this sort of syntax:
bool okToContinue = false;
object syncObject = new object();
//[....]
lock(syncObject)
while(okToContinue == false)
Monitor2.Wait(syncObject);
//[....]
lock(syncObject)
{
okToContinue = true;
Monitor2.Pulse(syncObject);
}
This is 99% of my rationale. However, there are performance considerations using static versus instance classes. At least that’s my understanding.
First, by making Monitor2 an instance class, there are three objects created on the heap: The Monitor2 instance and the two internal objects: sync and wait. If a 2nd, independent Monitor2 instance is created, there are now 6 objects on the heap. And so on. This takes up memory space and the CLR has to keep track of these objects for GC purposes.
Also, static method calls are slightly faster than instance method calls. Each time an instance method is called, a null reference check is required and the instance members (sync and wait) have to be located on the heap. Again, that is how I understand it. I’m not sure what the differences are in this regard in the context of the TinyCLR which evidently has a different way of doing these sorts of things than the full CLR does.
As a practical matter, I don’t generally worry about that in my normal coding environment - the performance savings are trivial in that context. And besides, proper design is far more important for good performance than these sorts of things in most situations. This is undoubtedly true in the TinyCLR world as well.
But since resources are at a premium in the microprocessor world, I see no harm in squeezing out what I can. It’s a good exercise in any event and I concede my understand may be flawed and my logic wrong as a result.
Here is my implementation. As you can see, I’ve deleted the Enter() and Exit() methods since they are redundant to the Monitor class. Oh, and I reversed the Enter/Exit calls in the Pulse(object) method from the original. I’m not sure if that was correct on my part.
using System;
using System.Threading;
using Microsoft.SPOT;
namespace MF.Threading
{
public class Program
{
#region test code
private static object s_Sync = new object();
private static bool s_okToContinue;
static void Main(string[] args)
{
new Thread
(
() =>
{
Debug.Print("Thread Start");
lock (s_Sync)
{
while (!s_okToContinue)
{
Monitor2.Wait(s_Sync);
}
}
Debug.Print("Thread Pulse");
}
).Start();
Thread.Sleep(100);
Debug.Print("Wait");
lock (s_Sync)
{
s_okToContinue = true;
Monitor2.Pulse(s_Sync);
}
Debug.Print("Zzzzz");
Thread.Sleep(Timeout.Infinite);
}
#endregion
}
/// <summary>
/// A Monitor implementation that adds Wait and Pulse
/// </summary>
public static class Monitor2
{
//private static readonly object _sync;
private static readonly AutoResetEvent s_wait = new AutoResetEvent(false);
/// <summary>
/// Wait for pulse.
/// </summary>
/// <param name="sync">The object to sync against.</param>
public static bool Wait(object sync)
{
if (sync == null)
{
throw new ArgumentNullException("sync");
}
return Wait(sync, -1, false);
}
//Wait for a specified time. -1 = infinity
private static bool Wait(object sync, int millisecondsTimeout, bool exitContext)
{
Monitor.Exit(sync); // Exit lock and wait for Pulse.
try
{
// If we timeout waiting for Pulse, return false.
return s_wait.WaitOne(millisecondsTimeout, exitContext);
}
finally
{
// Always re-enter the lock after wait.
Monitor.Enter(sync);
}
}
/// <summary>
/// Release a thread waiting on Wait
/// </summary>
/// <param name="sync">The object to sync against.</param>
public static void Pulse(object sync)
{
if (sync == null)
{
throw new ArgumentNullException("sync");
}
Monitor.Exit(sync);
try
{
s_wait.Set(); // Pulse 1 waiting.
}
finally
{
// Always re-enter the lock after set so caller retains the lock.
Monitor.Enter(sync);
}
}
}
}
Thanx,
Ralf