How to call a method on a running thread?

c#, multithreading

Solution

The target thread must run the code "on itself" - or it is just accessing the object across threads. This is done with some form of event dispatch loop on the target thread itself.

The SynchronizationContext abstraction can and does support this if the underlying provider supports it. For example in either WinForms or WPF (which themselves use the "window message pump") using `Post` will "run on the UI thread".

Basically, all such constructs follow some variation of the pattern:

// On "target thread"
while (running) {
   var action = getNextDelegateFromQueue();
   action();
}

// On other thread
postDelegateToQueue(actionToDoOnTargetThread);

It is fairly simple to create a primitive queue system manually - just make sure to use the correct synchronization guards. (Although I am sure there are tidy "solved problem" libraries out there; including wrapping everything up into a SynchronizationContext.)

Here is a primitive version of the manual queue. Note that there may be is1 a race condition.. but, FWIW:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace DogPark
{
    internal class DogPark
    {

        private readonly string _parkName;
        private readonly Thread _thread;
        private readonly ConcurrentQueue<Action> _actions = new ConcurrentQueue<Action>();
        private volatile bool _isOpen;

        public DogPark(string parkName)
        {
            _parkName = parkName;
            _isOpen = true;
            _thread = new Thread(OpenPark);
            _thread.Name = parkName;
            _thread.Start();
        }

        // Runs in "target" thread
        private void OpenPark(object obj)
        {
            while (true)
            {
                Action action;
                if (_actions.TryDequeue(out action))
                {
                    Program.WriteLine("Something is happening at {0}!", _parkName);
                    try
                    {
                        action();
                    }
                    catch (Exception ex)
                    {
                        Program.WriteLine("Bad dog did {0}!", ex.Message);
                    }
                }
                else
                {
                    // Nothing left!
                    if (!_isOpen && _actions.IsEmpty)
                    {
                        return;
                    }
                }

                Thread.Sleep(0); // Don't toaster CPU
            }
        }

        // Called from external thread
        public void DoItInThePark(Action action)
        {
            if (_isOpen)
            {
                _actions.Enqueue(action);
            }
        }

        // Called from external thread
        public void ClosePark()
        {
            _isOpen = false;
            Program.WriteLine("{0} is closing for the day!", _parkName);
            // Block until queue empty.
            while (!_actions.IsEmpty)
            {
                Program.WriteLine("Waiting for the dogs to finish at {0}, {1} actions left!", _parkName, _actions.Count);

                Thread.Sleep(0); // Don't toaster CPU
            }
            Program.WriteLine("{0} is closed!", _parkName);
        }

    }

    internal class Dog
    {

        private readonly string _name;

        public Dog(string name)
        {
            _name = name;
        }

        public void Run()
        {
            Program.WriteLine("{0} is running at {1}!", _name, Thread.CurrentThread.Name);
        }

        public void Bark()
        {
            Program.WriteLine("{0} is barking at {1}!", _name, Thread.CurrentThread.Name);
        }

    }

    internal class Program
    {
        // "Thread Safe WriteLine"
        public static void WriteLine(params string[] arguments)
        {
            lock (Console.Out)
            {
                Console.Out.WriteLine(arguments);
            }
        }

        private static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Home";

            var yorkshire = new DogPark("Yorkshire");
            var thunderpass = new DogPark("Thunder Pass");

            var bill = new Dog("Bill the Terrier");
            var rosemary = new Dog("Rosie");

            bill.Run();

            yorkshire.DoItInThePark(rosemary.Run);
            yorkshire.DoItInThePark(rosemary.Bark);

            thunderpass.DoItInThePark(bill.Bark);

            yorkshire.DoItInThePark(rosemary.Run);

            thunderpass.ClosePark();
            yorkshire.ClosePark();
        }

    }
}

The output should look about like the following - keep in mind that this will change when run multiples times due to the inherent nature of non-synchronized threads.

Bill the Terrier is running at Home!
Something is happening at Thunder Pass!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Bill the Terrier is barking at Thunder Pass!
Something is happening at Yorkshire!
Rosie is barking at Yorkshire!
Something is happening at Yorkshire!
Rosie is running at Yorkshire!
Thunder Pass is closing for the day!
Thunder Pass is closed!
Yorkshire is closing for the day!
Yorkshire is closed!

There is nothing preventing a dog from performing at multiple dog parks simultaneously.

1 There is a race condition present and it is this: a park may close before the last dog action runs.

This is because the dog park thread dequeues the action before the action is run - and the method to close the dog park only waits until all the actions are dequeued.

There are multiple ways to address it, for instance:

- The concurrent queue could first peek-use-then-dequeue-after-the-action, or

- A separate volatile isClosed-for-real flag (set from the dog park thread) could be used, or ..

I've left the bug in as a reminder of the perils of threading..

Problem

On a console application, i am currently starting an array of threads. The thread is passed an object and running a method in it. I would like to know how to call a method on the object inside the individual running threads. Dispatcher doesn't work. SynchronizationContext "Send" runs on the calling thread and "Post" uses a new thread. I would like to be able to call the method and pass parameters on a running thread on the target thread it's running on and not the calling thread. Update 2: Sample code ``` using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace CallingFromAnotherThread { class Program { static void Main(string[] args) { var threadCount = 10; var threads = new Thread[threadCount]; Console.WriteLine("Main on Thread " + Thread.CurrentThread.ManagedThreadId); for (int i = 0; i < threadCount; i++) { Dog d = new Dog(); threads[i] = new Thread(d.Run); threads[i].Start(); } Thread.Sleep(5000); //how can i call dog.Bark("woof"); //on the individual dogs and make sure they run on the thread they were created on. //not on the calling thread and not on a new thread. } } class Dog { public void Run() { Console.WriteLine("Running on Thread " + Thread.CurrentThread.ManagedThreadId); } public void Bark(string text) { Console.WriteLine(text); Console.WriteLine("Barking on Thread " + Thread.CurrentThread.ManagedThreadId); } } } ``` Update 1: Using synchronizationContext.Send results to using the calling thread ``` Channel created Main thread 10 SyncData Added for thread 11 Consuming channel ran on thread 11 Calling AddConsumer on thread 10 Consumer added consumercb78b. Executed on thread 10 Calling AddConsumer on thread 10 Consumer added consumer783c4. Executed on thread 10 ``` Using synchronizationContext.Post results to using a different thread ``` Channel created Main thread 10 SyncData Added for thread 11 Consuming channel ran on thread 11 Calling AddConsumer on thread 12 Consumer added consumercb78b. Executed on thread 6 Calling AddConsumer on thread 10 Consumer added consumer783c4. Executed on thread 7 ```

Original source

Related problems