限制C#中的方法

我有一个Game框架,其中有一个实现IBotInterface的Bots列表。 这些机器人是由用户定制的,唯一的限制是它们必须实现接口。

然后游戏调用机器人中的方法(希望并行),用于各种事件,如yourTurn和roundStart。 我希望机器人在被迫退出计算之前只花费有限的时间来处理这些事件。

我正在尝试的一种例子是:(其中NewGame是代表)

Parallel.ForEach(Bots, delegate(IBot bot) { NewGame del = bot.NewGame; IAsyncResult r = del.BeginInvoke(Info, null, null); WaitHandle h = r.AsyncWaitHandle; h.WaitOne(RoundLimit); if (!r.IsCompleted) { del.EndInvoke(r); } } ); 

在这种情况下,我被迫运行可能不会终止的EndInvoke()。 我想不出一种干净地中止线程的方法。

如果有某种forms的话会很棒

 try { bot.NewGame(Info); } catch (TimeOutException) { // Tell bot off. } finally { // Compute things. } 

但我不认为有可能制作这样的结构。

这样做的目的是优雅地处理具有意外无限循环或需要很长时间计算的AI。

解决这个问题的另一种可能方法是使用类似的东西(使用更多的c#和更少的伪代码)

 Class ActionThread { pulbic Thread thread { get; set; } public Queue queue { get; set; } public void Run() { while (true) { queue.WaitOne(); Act a = queue.dequeue(); a(); } } Class foo { main() { .... foreach(Bot b in Bots) { ActionThread a = getActionThread(b.UniqueID); NewGame del = b.NewGame; a.queue.queue(del); } Thread.Sleep(1000); foreach (ActionThread a in Threads) { a.Suspend(); } } } 

不是最干净的方式,但它会起作用。 (我会担心如何传递参数并稍后获取返回值)。

[进一步编辑]

我不太确定appdomain是什么,从它的外观我可以这样做,但它无法看到它会如何帮助

我希望不要指望恶意代码。 试图杀死其他机器人线程并不是赢得游戏的有效方式。 我只想给每个机器人一秒钟进行计算然后继续使用游戏流程,因此主要期望这里的代码速度慢或有问题。

我正试图看看我可以用Task做什么,慢慢地到达某个地方。

我会读到CAS可以做什么,谢谢你们

[更多编辑]

我的头疼,我似乎无法思考或编码了。 我正在建立一个消息传递系统到每个机器人的专用线程,并将暂停/睡眠这些线程

我已经决定使用完全sockets的服务器客户端系统。 这样客户端可以做任何想做的事情,如果它拒绝回复服务器消息,我会忽略它。 可惜它必须来到这个。

不幸的是,没有100%安全的方法可以干净地终止线程,就像你想要的那样。

有很多方法可以尝试这样做,但它们都有一些你可能想要考虑的副作用和缺点。

唯一清洁,安全和认可的方法是获得有问题的线程的合作并很好地问它 。 但是,如果您控制代码,这只是100%保证的方式。 既然你不是,那就不会。

这是问题所在。

  • 如果您执行Thread.Abort ,则可能会使appdomain处于不安全状态。 可能存在文件未打开,网络或数据库连接未打开,内核对象处于无效状态等。
  • 即使您将线程放入其自己的应用程序域,并在中止该线程后拆除该应用程序域,您也不能100%安全地认为您的进程将来不会出现问题。

让我们来看看为什么合作也不是100%。

假设有问题的线程经常需要调用你的库代码,以便在屏幕上绘图,或者诸如此类的东西。 您可以轻松地检查这些方法,并抛出exception。

但是,该exception可能被捕获并被吞噬。

或者有问题的代码可能进入一个无法调用你的库代码的无限循环,这使你回到原点,如何在没有合作的情况下干净地杀死线程。

我已经说过你不能。

但是,有一种方法可行。 您可以将机器人生成到自己的进程中,然后在超时时终止进程。 这将为您提供更高的成功机会,因为至少操作系统将在流程终止时处理它管理的所有资源。 你当然可以让这个过程在系统上留下损坏的文件,所以再次,它不是100%干净。

这是Joe Duffy的一篇博客文章,解释了很多关于这些问题的内容: 托管代码和异步exception加固 。

.NET 4.0 Task为您提供了相当大的取消灵活性。

在你已经通过CancelationTokenTask中运行委托,就像这样 。

这里有一个更全面的例子。

编辑

根据反馈意见,我认为正确的答案是遵循这一计划:

  1. 使用CAS限制AI,以便它无法访问线程原语或弄乱文件。

  2. 给它一个心跳回调,它在道德上有义务从长循环内部调用。 如果线程花费的时间过长,则此回调将引发exception。

  3. 如果AI超时,请为其记录一个弱移动,并给它一个很短的时间来调用该回调。 如果没有,只需将线程置于睡眠状态直到下一轮。 如果它永远不会突然消失,它将继续记录弱移动,直到该进程将其作为后台线程杀死它。

  4. 利润!

一种选择当然是自己启动新线程 ,而不是依赖于BeginInvoke。 您可以通过Thread.Join实现超时,并且(毫不客气地)通过Thread.Abort杀死线程(如有必要)。

正如我在@ Lasse的回答中所提到的,你真的应该考虑使用代码访问安全来限制允许机器人在系统上执行的操作。 您可以真正限制允许机器人执行的操作(包括删除它们访问线程API的能力)。 可能值得考虑将每个机器人视为病毒,因为您无法控制它对您的系统可以做什么,并且我假设您的游戏将作为完全信任的应用程序运行。

也就是说,如果你限制每个机器人使用CAS做什么,你可以终止线程而不用担心破坏你的系统。 如果机器人卡在无限循环中,我怀疑你唯一的办法是终止线程,因为它会以编程方式永远无法访问任何检查Thread.Abort信号的代码。

这篇MSDN文章可能会为您提供有关如何使用TerminateThread函数更有效地终止失控线程的一些指导。

你真的必须尝试所有这些事情并向自己certificate哪个可能是最好的解决方案。

或者,如果这是一个竞争性游戏和机器人作者必须公平,你是否考虑让游戏给每个机器人一个“转弯令牌”,机器人必须呈现它想要调用的每个动作以及当游戏翻转时,它为所有机器人提供了新的令牌。 这可能让你现在只需担心失控的线程,而不是担心轮到他们的机器人。

编辑

只是为人们添加一个地方看看安全许可标志枚举会让你了解从哪里开始。 如果删除SecurityPermissionFlag.ControlThread权限(作为示例,仅授予执行代码和排除ControlThread的权限),您将删除它们

能够在线程上使用某些高级操作。

我不知道无法进行的操作的程度,但周末的一个有趣的练习可以解决这个问题。

我认为唯一的问题是倒置条件。 只有在任务成功完成时才应调用EndInvoke

粗略的建议:

 var goodBots = new List(); var results = new IAsyncResult[Bots.Count]; var events = new WaitHandle[Bots.Count]; int i = 0; foreach (IBot bot in Bots) { NewGame del = bot.NewGame; results[i] = del.BeginInvoke(Info, null, null); events[i++] = r.AsyncWaitHandle; } WaitAll(events, RoundLimit); var goodBots = new List(); for( i = 0; i < events.Count; i++ ) { if (results[i].IsCompleted) { goodBots.Add(Bots[i]); EndInvoke(results[i]); results[i].Dispose(); } else { WriteLine("bot " + i.ToString() + " eliminated by timeout."); } } Bots = goodBots; 

更好的方法是明确地为每个机器人启动一个线程,这样你就可以暂停行为不当的机器人(或者降低它们的优先级),这样它们就不会继续从好机器人那里抢走CPU时间。

另一个设计选项可能是允许每个机器人成为它自己的进程,然后使用IPC机制来交换数据。 您通常可以在没有任何可怕后果的情况下终止进程。