有什么方法可以让委托在特定线程上运行吗?
说我有:
CustomDelegate del = someObject.someFunction;
Thread dedicatedThread = ThreadList[x];
我可以有一个一致的后台长时间运行线程,并在需要时调用我自己的委托吗?每次都必须是同一个线程。
[编辑]
我希望它位于专用线程上的原因是时间,因为我打算在其上运行委托并在
y
毫秒后挂起线程,并在我在其上运行另一个委托时恢复线程。我看这是不可能的。我将有一个委托队列,让线程的主函数从中读取并运行。
为了澄清一个具体的例子,我有一个带有一堆玩家线程的游戏系统。我希望每个玩家线程都为其上的游戏事件运行事件处理程序。如果事件处理程序花费太多时间,我希望能够通过挂起其线程来暂停该特定播放器,直到下一个事件发生。
因此,有了一个可以运行多个事件处理程序的专用线程,我可以暂停特定玩家的 AI,以防它出现错误或花费太长时间。
我认为最好的解决方案是使用
Task
对象并将它们排队到运行单个线程的 StaThreadScheduler 。
或者,您可以使用
Nito.Async中的
ActionThread
创建一个带有内置 Action
委托队列的普通线程。
但是,这些都不会直接解决另一个需求:“暂停”一个操作并继续另一个操作的能力。为此,您需要在每个操作中撒上“同步点”,并有办法保存其状态、重新排队并继续下一个操作。
所有这些复杂性都非常接近线程调度系统,因此我建议退一步并进行更多的重新设计。您可以允许每个操作都排队到
ThreadPool
(我建议让每个操作都是一个 Task
对象)。您仍然需要添加“同步点”,但您只需暂停(阻止)它们,而不是保存状态并重新对它们进行排队。
通常,我建议只使用线程池或
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 );
不幸的是,实际上没有内置任何东西可以在任何通用线程上执行此操作。您可以通过创建一个包装 Thread 并实现 ISynchonizeInvoke 的类来实现此目的。
一个简单的方法是在专用线程上创建一个事件处理队列,正如 LBushkin 提到的那样。我建议使用
Queue<Action>
类并直接调用 Action 委托。您可以使用匿名委托操作完成所需的大多数任务。
最后,作为警告,我建议您在专用线程上使用 Semaphore 或 EventWaitHandle 而不是 Thread.Sleep。这绝对比在不必要时一遍又一遍地执行后台循环更友好。
对于您创建的线程,您只能在创建它们时指定 ThreadStart 委托。没有规定将不同的委托注入到创建的线程中。线程池的不同之处在于,它允许您将委托提交给之前创建的线程(它代表您启动)。
尚不清楚您要解决什么问题。您试图通过尝试在一个线程上运行多个委托来实现(或避免)什么?
具有同步点的模式。其特点之一是工作线程可以在阻塞工作调用之前或之后启动。这是一个名为 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;