Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# non-UI cross-thread invocation

I'm trying to make cross-threaded calls in C#.

Whenever I invoke the methods of an object created in the context of thread A from a static method called from thread B, the method always runs in thread B. I don't want that, I want it run on the same thread as the thread A object whose methods I am calling.

Invoke works fine for UI calls and I've read dozens of articles and SO answers relating to different ways of making cross-threaded Forms/WPF calls. However whatever I try (event handling, delegates, etc) Thread A's object's method will always run in Thread B if it is invoked by Thread B.

What part of the library should I be looking in to solve this? If it's relevant, Thread B currently 'spins', reads from a network port and occasionally invokes Thread A's object's method through a delegate that was created in Thread A and passed in using a ParameterizedThreadStart.

I'm not looking to change paradigm, just send a message (a request to invoke a method) from one thread (Thread B) to another (Thread A).

EDIT:

My question was 'what part of the library should I be looking in to solve this?' The answer appears to be none. If I want to clearly delineate consumption and polling I'll have to write my own code to handle that.

like image 738
Rushyo Avatar asked Apr 28 '26 11:04

Rushyo


2 Answers

Whenever I invoke the methods of an object running on thread A

Objects don't run on threads.

In order for this to work, you will have to create some kind of queue you can shove a delegate into that will be routinely checked thread A's main loop. Something like this, assuming that Something.MainThreadLoop is the entry point for thread A:

public class Something
{
    private Queue<Action> actionQueue = new Queue<Action>();

    private volatile bool threadRunning = true;

    public void RunOnThread(Action action)
    {
        if (action == null)
            throw new ArgumentNullException("action");

        lock (actionQueue)
            actionQueue.Enqueue(action);
    }

    public void Stop()
    {
        threadRunning = false;
    }

    private void RunPendingActions()
    {
        while (actionQueue.Count > 0) {
            Action action;

            lock (actionQueue)
                action = actionQueue.Dequeue();

            action();
        }
    }

    public void MainThreadLoop()
    {
        while (threadRunning) {
            // Do the stuff you were already doing on this thread.

            // Then, periodically...
            RunPendingActions();
        }
    }
}

Then, given a reference to a Something object, you could do this:

something.RunOnThread(() => Console.WriteLine("I was printed from thread A!"));
like image 115
cdhowie Avatar answered May 01 '26 00:05

cdhowie


Code runs on threads. Objects aren't (generally - see thread local) bound to a particular thread. By doing WinFormControl.Invoke or WPFControl.Invoke, you are posting a message to the Message Pump or Dispatcher respectively, to run some code at a later date.

The message pump is something like this:

Message message;
while(GetMessage(&message))
{
    ProcessMessage(message);
}

Microsoft has specifically built their UI controls and projects to allow the posting of messages across threads. Calling a method from thread A will always execute that method on thread A, even if it ends up doing some kind of asynchronous work and returning early.

Edit:

What it is I think you need is the Producer Consumer pattern.

http://msdn.microsoft.com/en-us/library/yy12yx1f(VS.80).aspx

Forget about consuming the messages from your main thread, which is what it sounds like you want to do. Consume from thread C.

Thread A is doing 'much more important things'. Thread B is spinning, listening for messages. Thread C is consuming those messages.

No need for marshalling across threads.

like image 36
Josh Smeaton Avatar answered May 01 '26 01:05

Josh Smeaton