在特定线程上调用委托 C#

问题描述 投票:0回答:5

有什么方法可以让委托在特定线程上运行吗?

说我有:

CustomDelegate del = someObject.someFunction;
Thread dedicatedThread = ThreadList[x];

我可以有一个一致的后台长时间运行线程,并在需要时调用我自己的委托吗?每次都必须是同一个线程。

[编辑]

我希望它位于专用线程上的原因是时间,因为我打算在其上运行委托并在

y
毫秒后挂起线程,并在我在其上运行另一个委托时恢复线程。我看这是不可能的。我将有一个委托队列,让线程的主函数从中读取并运行。

为了澄清一个具体的例子,我有一个带有一堆玩家线程的游戏系统。我希望每个玩家线程都为其上的游戏事件运行事件处理程序。如果事件处理程序花费太多时间,我希望能够通过挂起其线程来暂停该特定播放器,直到下一个事件发生。

因此,有了一个可以运行多个事件处理程序的专用线程,我可以暂停特定玩家的 AI,以防它出现错误或花费太长时间。

c# multithreading delegates
5个回答
3
投票

我认为最好的解决方案是使用

Task
对象并将它们排队到运行单个线程的 StaThreadScheduler

或者,您可以使用

Nito.Async
中的 ActionThread 创建一个带有内置
Action
委托队列的普通线程。

但是,这些都不会直接解决另一个需求:“暂停”一个操作并继续另一个操作的能力。为此,您需要在每个操作中撒上“同步点”,并有办法保存其状态、重新排队并继续下一个操作。

所有这些复杂性都非常接近线程调度系统,因此我建议退一步并进行更多的重新设计。您可以允许每个操作都排队到

ThreadPool
(我建议让每个操作都是一个
Task
对象)。您仍然需要添加“同步点”,但您只需暂停(阻止)它们,而不是保存状态并重新对它们进行排队。


2
投票

通常,我建议只使用线程池或

BackgroundWorker
类 - 但这些并不能保证工作会发生在任何特定线程上。目前还不清楚为什么你关心哪个线程运行该作品,但假设它确实很重要......

您必须通过某种共享内存(例如队列)传递

Delegate
对象。后台线程必须监视此队列,当委托存在时将其从其中拉出,然后执行它们。

如果事实证明线程池可以运行您的代码,您始终可以使用委托的

BeginInvoke
方法来执行此操作:

// wrap your custom delegate in an action for simplicity ...
Action someCode = () => yourCustomDelegate( p1, p2, p3, ... );
// asynchronously execute the currying Action delegate on the threadpool...
someCode.BeginInvoke( someCode.EndInvoke, action );

2
投票

不幸的是,实际上没有内置任何东西可以在任何通用线程上执行此操作。您可以通过创建一个包装 Thread 并实现 ISynchonizeInvoke 的类来实现此目的。

一个简单的方法是在专用线程上创建一个事件处理队列,正如 LBushkin 提到的那样。我建议使用

Queue<Action>
类并直接调用 Action 委托。您可以使用匿名委托操作完成所需的大多数任务。

最后,作为警告,我建议您在专用线程上使用 Semaphore 或 EventWaitHandle 而不是 Thread.Sleep。这绝对比在不必要时一遍又一遍地执行后台循环更友好。


0
投票

对于您创建的线程,您只能在创建它们时指定 ThreadStart 委托。没有规定将不同的委托注入到创建的线程中。线程池的不同之处在于,它允许您将委托提交给之前创建的线程(它代表您启动)。

尚不清楚您要解决什么问题。您试图通过尝试在一个线程上运行多个委托来实现(或避免)什么?


0
投票

具有同步点的模式。其特点之一是工作线程可以在阻塞工作调用之前或之后启动。这是一个名为 Watin 的 COM 对象包装器,用于操作 Internet Explorer 的实例,它就像一个哭泣的婴儿,对上下文极其敏感。改进可能是删除

Thread.Sleep()
,可能添加
AutoResetEvent
,这将在某些情况下极大地提高性能。

这种模式的想法来自 Justin Breitfeller、Stephen Cleary 和 LBushkin 的回答。

    private Instantiate()
    {
        BrowserQueue = new ConcurrentQueue<BrowserAction>();
        BrowserThread = new Thread(new ThreadStart(BrowserThreadLoop));
        BrowserThread.SetApartmentState(ApartmentState.STA);
        BrowserThread.Name = "BrowserThread";
        BrowserThread.Start();
    }
    private static void BrowserThreadLoop()
    {
        while (true)
        {
            Thread.Sleep(500);
            BrowserAction act = null;
            while (Instance.BrowserQueue.TryDequeue(out act))
            {
                try
                {
                    act.Action();
                }
                catch (Exception ex) { }
                finally
                {
                    act.CompletionToken.Set();
                }
            }
        }
    }
    public static void RunBrowserAction(Action act)
    {
        BrowserAction ba = new BrowserAction() { Action = act, CompletionToken = new ManualResetEvent(false) };
        Instance.BrowserQueue.Enqueue(ba);
        ba.CompletionToken.WaitOne();
    }
    public class BrowserAction
    {
        public Action Action { get; set; } = null;
        public ManualResetEvent CompletionToken { get; set; } = null;
    }
    ConcurrentQueue<BrowserAction> BrowserQueue;
© www.soinside.com 2019 - 2024. All rights reserved.