Rushyo
Rushyo

Reputation: 7604

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.

Upvotes: 2

Views: 3061

Answers (4)

CodingGorilla
CodingGorilla

Reputation: 19842

What you're going to have to do is roll a sort of Queue and have Thread A watch that queue for work. When Thread A sees new work enter the queue, it can dequeue it and do the work, then return to waiting for more.

Here's some pseudo-code:

    public class ThreadAQueue
    {
        private Queue<delegate> _queue;
        private bool _quitWorking;

        public void EnqueueSomeWork(delegate work)
        {
            lock(_queue) 
            {
                _queue.Enqueue(work);
            }
        }

        private void DoTheWork()
        {
            while(!quitWorking) 
            {
                delegate myWork;

                lock(_queue)
                {
                    if(_queue.Count > 1)
                        myWork = _queue.Dequeue();
                }

                myWork();
            }
        }
    }

Upvotes: 1

Josh Smeaton
Josh Smeaton

Reputation: 48710

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.

Upvotes: 3

Dr. Wily&#39;s Apprentice
Dr. Wily&#39;s Apprentice

Reputation: 10280

EDIT: I think you probably want to use the System.Threading.AutoResetEvent class. The MSDN documentation has a decent example of one thread waiting on the other that I think is similar to what you are trying to do: http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent.aspx In particular, pay attention to the calls to trigger.WaitOne() and trigger.Set()

EDIT2: Added option #3 after reading new comment from OP.

"Whenever I invoke the methods of an object running on thread A ..." - An object doesn't "run" on a thread and isn't really owned by any thread, regardless of what thread created the object.

Given that your question is regarding "non-UI cross-thread invocation", I assume you are already familiar with "UI cross-thread invocation". I can see how WinForms would give you an impression that a thread owns an object and that you need to "send a message" to a thread in order to make it do something.

WinForm control objects are kind of a special case in that they simply don't function properly if you interact with them with a thread that isn't the one that created them, but that's not caused by the way that threads and objects interact.

Anyway, on to addressing your question.

First, a question to clarify the problem: You've mentioned what Thread B is doing, but what is Thread A doing prior to being "invoked" by Thread B?

Here are a couple of ideas that I think are along the lines of what you want to do:

  1. Don't create Thread A until you need to. Instead of having Thread B "send a message to Thread A", rather have Thread B create Thread A (or call it Thread C if you prefer) and make it start executing at that time.

  2. If you need Thread A to already exist and you only want Thread A to handle Thread B's events one at a time, you could have Thread A wait until it receives a notification from Thread B. Take a look at the System.Threading.WaitHandle class (derived classes of interest are ManualResetEvent and AutoResetEvent).

    Thread A will at some point call WaitHandle.WaitOne(), which will cause it to pause and wait until Thread B calls WaitHandle.Set() on the same WaitHandle object.

  3. If Thread A is busy doing other things, then you might want to set up some kind of flag variable. Similar to the WaitHandle concept in #2, but instead of causing Thread A to pause, you just want Thread B to set a flag (perhaps just a boolean variable) that will signal to Thread A that it needs to do something. While Thread A is busy doing other things, it can periodically check that flag to decide whether or not there is work that needs to be done.

Does the method that Thread A will execute on your object require any input from Thread B? Then before Thread B calls WaitHandle.Set(), have it stick some data into a queue or something. Then, when Thread A is "activated", it can retrieve that data from the queue and proceed to execute the object's method using that data. Use a lock mechanism (i.e. the C# lock statement) to synchronize access to the queue.

Upvotes: 1

cdhowie
cdhowie

Reputation: 168958

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!"));

Upvotes: 6

Related Questions