3

I have a created a class to facilitate simple text message transmission over a queue

public class TextQueueTransmitter
{
    private Func<IModel> channelProvider;
    private IBasicProperties props;
    private string queue;

    public TextQueueTransmitter(Func<IModel> channelProvider, string queue)
    {
        this.queue = queue;
        this.channelProvider = channelProvider;
        
         //will be called once in a thread
        this.channelProvider().QueueDeclare(queue: queue,
                             durable: false,
                             exclusive: false,
                             autoDelete: false,
                             arguments: null);
        props = this.channelProvider().CreateBasicProperties();
    }

    public void SendMessage(string message)
    {
        var body = Encoding.UTF8.GetBytes(message);
        props.DeliveryMode = 1;
        
        //will be called multiple times not necessarily in the same thread
        channelProvider().BasicPublish(exchange: "",
            routingKey: queue,
            basicProperties: null,
            body: body);
    }
}

Initialization will be like

IConnection connection = ...
ThreadLocal channelThreadLocal = new ThreadLocal<IModel>(connection.CreateModel);
TextQueueTransmitter transmitter = new TextQueueTransmitter(() => channelThreadLocal.Value, "queue name");

previously I was directly passing IModel to the transmitter but after reading here that IModel is not thread safe, I realized I had to rethink it if I wanted to make TextQueueTransmitter thread safe.

My question is are there any implications of just wrapping IModel in ThreadLocal? For example if SendMessage() is called in a thread other than the one that constructed the object, then QueueDeclare and BasicPublish will be called on different threads. Would that cause any problems?

Update: Resource disposal

As Samuel pointed out in his answer, there is the channel disposal issue that I have thought of but I forgot to write about. What I was planing to do, is call Dispose() of ThreadLocal on program's termination, hoping that this will call Dispose() of the underlying IModels. Though I will have to verify since ThreadLocal documentation does not specify if it disposes the undelying objects (should they implement IDisposable).

This imposes the danger of "dangling" connections i.e. having connections for a specific thread no longer running. Though I don't consider it grave problem since all TextQueueTransmitter instances are intended to be long lived objects and I don't create threads excessively.

Community
  • 1
  • 1
Stelios Adamantidis
  • 1,866
  • 21
  • 36

1 Answers1

2

The biggest problem I'd forsee is not disposing of the models and leaking resources. You need to have control over when they are created and dispose of them appropriately.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • +1 for reminding me that very important thing that I forgot. :) I've updated my question so you might want to revisit and probably elaborate your thought. – Stelios Adamantidis Oct 17 '17 at 13:06