Search code examples
c#thread-safetydispatcher

Forcing single-thread access to a resource


Currently I have multiple resources in a project that can only be accessed from a single thread. One example is a ZeroMQ server and another is a Mitsubishi PLC communication module that's not threadsafe.

Right now I create a seperate thread for any resource requiring single-threaded access. I haven't wrapped this in a class or anything, so I have a ThreadStart (void) method and a couple of properties in every class requiring coordinated access to a resource. I then use a dispatcher combined with Invoke / BeginInvoke to force access from the seperate Thread only.

Is there some class already available that can coordinate access to a resource from a single thread?

Here's some sample code that will get me a Dispatcher right now:

object theLock = new object();
Dispatcher dispatcher = null;

lock (theLock)
{
    new Thread(new ThreadStart(() =>
    {
        lock (theLock)
        {
            dispatcher = Dispatcher.CurrentDispatcher;
            Monitor.Pulse(theLock);
        }
        Dispatcher.Run();
    })).Start();

    Monitor.Wait(theLock);
}

dispatcher.Invoke(...);

I can wrap this code in a class just to create a thread and dispatcher whenever I need one, but I can't imagine this being the best solution to coordinate access to a thread-unsafe resource.


Solution

  • Did you try using Task and BlockingCollection? You could enqueue in there tasks that need to access the resource, and start a task that dequeue and run the task, one after each others so there is no race condition?

    static BlockingCollection<Action<StringBuilder>> queue = new BlockingCollection<Action<StringBuilder>>();
    
    //Your thread unsafe resource...
    static StringBuilder resource = new StringBuilder();
    
    static void Main(string[] args)
    {
        Task.Factory.StartNew(() =>
        {
            while (true)
            {
                var action = queue.Take();
                action(resource);
            }
        });
    
        //Now to do some work you simply add something to the queue...
        queue.Add((sb) => sb.Append("Hello"));
        queue.Add((sb) => sb.Append(" World"));
    
        queue.Add((sb) => Console.WriteLine("Content: {0}", sb.ToString()));
    
        Console.ReadLine();
    }
    

    It's all static and a bit ugly and certainly needs rework, but it should be ok. The idea is that you enqueue lambdas that you want to execute, and they will be executed one after each other so you don't have to synchronize on the resource.