views:

224

answers:

4

What is the most efficient way to monitor a queue.

The follwoing piece of code is the biggest hog of resources :

/// <summary>
/// Starts the service.
/// </summary>
private void StartService()
{
    while (true)
    {
        //the check on count is not thread safe
        while (_MessageQueue.Count > 0)
        {
            Common.IMessage message;
            // the call to GetMessageFromQueue is thread safe
            if (_MessageQueue.GetMessageFromQueue(out message) == true)
            {
                if (message.RoutingInfo == Devices.Common.MessageRoutingInfo.ToDevice)
                {
                    _Port.SerialPort.WriteLine(message.Message);
                }
                if (message.RoutingInfo == Devices.Common.MessageRoutingInfo.FromDevice)
                {
                    OnDeviceMessageReceived(new Common.DeviceMessageArgs(message.Message));
                }
            }
        }
    }
}

Start Service runs on background thread , the call to _MessageQueue.Count is not thread safe , I am not locking on count in MessageQueue . I do however lock on the implementation of _MessageQueue.GetMessageFromQueue . Is the way I have gone about this efficient? Should I rather raise an event Every time the queue goes from a Count of 0 to greater than zero ?

+3  A: 

You should probably include some type of thread sleep into that method, otherwise its going to be using 100% CPU. Alternatively, you could create a wait handle, and set it when you add a message to the queue.

David
No sleeps unless you have no control whatsoever over the producer. Use events or signals to do proper waiting/notifications.
Mats Fredriksson
Why Mats? I have control over the producer. Should I be raising an event when an item is added to the queue? I will look into waithandle.
Mule
+1. See my answer for an example.
dtb
+1  A: 

If I haven't missed anything - you're doing a busy wait on the _messageQueue.Count property. I would try doing something like is done at: http://msdn.microsoft.com/en-us/library/yy12yx1f.aspx

RA
A: 

Instead of actively polling the queue you can consume MSMQ message directly into WCF with a NetMsmqBinding

http://msdn.microsoft.com/en-us/library/ms789008.aspx

Hope this helps.

blu
+1  A: 

Is _MessageQueue only used from your code? Then you could wrap it in a class like this:

public class BlockingMessageQueue {
  private readonly MyMessageQueue queue;
  private readonly Semaphore signal;

  public BlockingMessageQueue(MyMessageQueue queue) {
    this.queue = queue;
    this.signal = new Semaphore(0, int.MaxValue);
  }

  public void Enqueue(IMessage message) {
    lock (this.queue) {
      this.queue.Send(message);
    }
    this.signal.Release();
  }

  public IMessage Dequeue() {
    this.signal.WaitOne();
    IMessage message;
    lock (this.queue) {
      var success = this.queue.GetMessageFromQueue(out message);
      Debug.Assert(success);
    }
    return message;
  }
}

Dequeue will block until a message is available, so there are no wasted cycles if no message is available.

Usage example:

var queue = new BlockingMessageQueue(_MessageQueue);

while (true) {
  var message = queue.Dequeue();

  if (message.RoutingInfo == Devices.Common.MessageRoutingInfo.ToDevice)
  {
    _Port.SerialPort.WriteLine(message.Message);
  }
  else if (message.RoutingInfo == Devices.Common.MessageRoutingInfo.FromDevice)
  {
    OnDeviceMessageReceived(new Common.DeviceMessageArgs(message.Message));
  }
}
dtb
Thank you so much , I will definetly give this a go . I will let you know how it performs. Thanks
Mule