如何在正在运行的线程上调用方法? [英] How to call a method on a running thread?

查看:63
本文介绍了如何在正在运行的线程上调用方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在控制台应用程序上,我目前正在启动一个线程数组.线程被传递一个对象并在其中运行一个方法.我想知道如何在各个运行线程内的对象上调用方法.

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.

调度程序不工作.SynchronizationContext "Send" 在调用线程上运行,而 "Post" 使用一个新线程.我希望能够调用该方法并在它运行的目标线程上的运行线程上传递参数,而不是调用线程.

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.

更新 2:示例代码

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);
        }
    }
}

更新 1:使用同步上下文.将结果发送到使用调用线程

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

推荐答案

目标线程必须在自身"上运行代码 - 或者它只是跨线程访问对象.这是通过目标线程本身的某种形式的事件调度循环来完成的.

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.

SynchronizationContext 抽象可以并且确实支持这个如果底层提供者支持它.例如,在 WinForms 或 WPF(它们本身使用窗口消息泵")中,使用 Post 将在 UI 线程上运行".

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

手动创建原始队列系统相当简单 - 只需确保使用正确的同步保护.(尽管我确信那里有整洁的已解决问题"库;包括将所有内容都包装到 SynchronizationContext 中.)

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.)

这是手动队列的原始版本.请注意,可能1竞争条件..但是,FWIW:

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存在一个竞争条件,它是这样的:一个公园可能在最后个狗行动开始之前关闭.

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

这是因为 dog park 线程会在 action 运行之前将 action 出列 - 而关闭 dog park 的方法只会等到所有操作都出列.

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:

  • 并发队列可以先 peek-use-then-dequeue-after-the-action,或者
  • 可以使用单独的易失性 isClosed-for-real 标志(从狗公园线程设置),或者..

我已经把 bug 留在了,以提醒线程的危险..

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

这篇关于如何在正在运行的线程上调用方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆