【问题标题】:Invoke a delegate on a specific thread C#在特定线程 C# 上调用委托
【发布时间】:2010-08-13 22:26:08
【问题描述】:

有没有办法让委托在特定线程上运行?

说我有:

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

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

[编辑]

我希望它在专用线程上的原因是时间是我打算在其上运行委托并在y 毫秒后暂停线程,并在我在其上运行另一个委托时恢复线程。我看这是不可能的。我将有一个委托队列,并让线程的主要功能从中读取和运行。

用一个具体的例子来说明,我有一个包含一堆玩家线程的游戏系统。我希望每个玩家线程在其上运行游戏事件的事件处理程序。如果事件处理程序花费太多时间,我希望能够通过暂停其线程来暂停该特定播放器直到下一个事件。

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

【问题讨论】:

    标签: c# multithreading delegates


    【解决方案1】:

    我认为最好的解决方案是使用Task 对象并将它们排队到运行单个线程的StaThreadScheduler。

    或者,您可以使用Nito.Async 中的ActionThread 来创建一个带有Action 委托的内置队列的普通线程。

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

    所有这些复杂性都非常接近线程调度系统,因此我建议退后一步,进行更多的重新设计。您可以允许每个操作排队到ThreadPool(我建议每个操作都是Task 对象)。您仍然需要散布“同步点”,但您只需暂停(阻止)它们,而不是保存状态并重新排队它们。

    【讨论】:

    • 感谢您指出 ActionThread,它看起来非常有用。我需要封装我在线程中几乎无法控制的外部代码。我所拥有的只是外部代码的代表。如果我可以确保对象在一个特定线程中,我可以通过挂起线程来“暂停”该对象。我认为线程池不会让我很好地“暂停”对象。
    • 一句警告:暂停/恢复线程可能(甚至可能)意外导致死锁。如果可能,请更改外部代码。如果这不可能,那么考虑使用线程优先级而不是挂起/恢复。
    【解决方案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 );
    

    【讨论】:

      【解决方案3】:

      不幸的是,在任何通用线程上都没有内置任何东西可以做到这一点。您可以通过创建一个包装 Thread 并实现 ISynchonizeInvoke. 的类来完成此操作

      正如 LBushkin 所提到的,一种简单的方法是在专用线程上创建一个事件处理队列。我建议使用 Queue<Action> 类并直接调用 Action 委托。您可以使用匿名委托操作完成大部分您需要的任务。

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

      【讨论】:

      • 谢谢,我打算一遍又一遍地运行后台循环。我将研究一个事件等待处理程序
      • 我同意关于Thread.Sleep的警告;但是,操作应该实现SynchronizationContext,而不是过时的ISynchronizeInvoke。
      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        具有同步点的模式。其中一个特点是工作线程可以在阻塞工作调用之前或之后启动。这是一个名为 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;
        

        【讨论】:

          猜你喜欢
          • 2014-01-06
          • 1970-01-01
          • 1970-01-01
          • 2015-10-24
          • 1970-01-01
          • 2011-04-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多