This time we are going to look at the method queuing approach to producer-consumer. Like last time, we will use C# and asynchronous programming patterns. This implementation requires two queues, one for messages that arrive without an outstanding Consume method call, and a second queue for outstanding Consume method invocations. Despite the extra queue, to me it is both simpler and more efficient than the message queue only implementation.
public void Produce(string message)
{
AsyncResult ar = null;
lock (protect)
{
if (methods.Count > 0)
{
ar = methods.Dequeue();
}
else
{
// no waiting method calls
messages.Enqueue(message);
}
}
if (ar != null)
ar.Complete(message);
}
public string Consume(TimeSpan timeout)
{
IAsyncResult result = BeginConsume(timeout, null, null);
return EndConsume(result);
}
public IAsyncResult BeginConsume(TimeSpan timeout, AsyncCallback callback, object state)
{
AsyncResult ar = new AsyncResult(callback, state, timeout);
string message = null;
lock (protect)
{
if (messages.Count > 0)
{
message = messages.Dequeue();
}
else
{
methods.Enqueue(ar);
}
}
if (message != null)
ar.CompleteSynchronously(message);
return ar;
}
public string EndConsume(IAsyncResult result)
{
string message = (string)AsyncResult.End(result);
return message;
}
This is about the same amount of code as the original approach, but doesn't waste a thread to wait for new messages when clients use the asynchronous methods. I also find it much simpler to reason about, and thus less likely to have synchronization issues.
-randy
Comments