ose
ose

Reputation: 4075

Invoke a method on an arbitrary thread

I have a scenario where I have a main thread which uses a whole bunch of other threads from the threadpool that do some work.

In the main thread I am using an object which is not thread safe (3rd party component, so it's not feasible to change this). The threadpool threads need to invoke methods on this non-thread-safe object. In the main thread I have an object representing the thread on which the 3rd party component is declared:

private ThirdPartyComponent third_party_component;
private Thread mainThread;

In the initialization method on the main thread, I save a reference to the "safe" thread for accessing the 3rd party component:

mainThread = Thread.CurrentThread;

What I am trying to do is create some method which will "dispatch" the invocation of the 3rd party component to the correct thread, something like this:

private void DoTheWork()
{
    if(Thread.CurrentThread != mainThread)
    {
         mainThread.Invoke( () => { third_party_component.DoItThreadSafe(); } );
    }
}

I have regularly used this pattern for updating UI code on the main thread using Control.Invoke, but the Thread object which I have saved (mainThread) does not have an Invoke method.

Is there something special about the UI thread that allows this to take place? Or am I missing something on the Thread object? What is the best way to get DoTheWork() to run on the main thread?

(Edit: FWIW in this application mainThread will be the main Winforms GUI thread, but I am hoping to find a solution which works on any thread and does not rely on the main thread being the GUI thread.)

Upvotes: 0

Views: 296

Answers (1)

JonasH
JonasH

Reputation: 36361

Normally when talking about non-threadsafe objects it simply means it cannot be called by multiple threads concurrently. If so it should be safe to simply lock the non-threadsafe object and call it from whatever thread you are using.

It is possible to write objects that can only be called by the thread that created the object thread. If so perhaps something like this could work:

public class TaskManager<T>
{
    private readonly Func<T> constructor;
    private BlockingCollection<Action<T>> queue = new BlockingCollection<Action<T>>(new ConcurrentQueue<Action<T>>());

    public TaskManager(Func<T> constructor)
    {
        this.constructor = constructor;
        var thread = new Thread(ThreadMain);
        thread.Start();
    }

    private void ThreadMain()
    {
        var obj = constructor();
        foreach(var action in queue.GetConsumingEnumerable())
        {
            action(obj);
        }
    }

    public void ScheduleWork(Action<T> work) => queue.Add(work);
    public void CompleteAdding() => queue.CompleteAdding();
}

Disclaimer: Not tested, no error handling, no handling of disposable objects etc.

Upvotes: 2

Related Questions