【问题标题】:Which is the most recommended thread pattern for game AI and how to implement it? [closed]游戏 AI 最推荐的线程模式是什么?如何实现? [关闭]
【发布时间】:2011-12-18 20:41:03
【问题描述】:

我是 C# 线程的新手,尽管阅读了很多关于线程的理论,但它在实践中对我没有多大帮助。

我想为跳棋游戏编写 AI 函数(minmax alphabeta)并在不同的线程中执行。

有 4 个选项:常规 Tread、线程池、异步委托、BackgroundWorker。

BackgroundWorker 对我来说似乎很理想,它具有完成委托,因此我可以运行“makemove”函数,该函数实际上会在棋盘上进行计算的移动并更新进度条。

我对此有 3 个问题:

  1. BackgroundWorker 真的是这个案例的最佳解决方案吗?

  2. BackgroundWorker 在线程池中执行,这样做有什么好处?当你有许多不同的线程时,总是说线程池很好,而我的情况并非如此。

  3. 我看到的所有代码示例都太简单了,只展示了如何创建一个这样的线程。在我的程序中,每次轮到计算机时我都需要运行这个函数,所以我可能需要杀死前一个线程并启动一个新线程。实现这一切的正确方法是什么?

任何帮助将不胜感激。

【问题讨论】:

  • 你能解释一下为什么你想让 AI 在自己的线程中运行吗?为什么不在 UI 线程上运行 AI?
  • 因为那样表格会冻结。
  • 仅当 AI 长时间运行时。为什么不构建您的 AI,使其运行时间少于 10 毫秒,记住它在哪里,允许表单处理事件,然后再运行 10 毫秒,重复?不需要额外的线程。十毫秒大约是四千万个处理器周期;当然,您可以在 4000 万个周期内完成相当多的工作。
  • 从来没想过。假设我必须在 10 种不同的移动中进行选择,所以我会分别计算每一种移动并将其排名放入某个数据结构中。但是如果计算一些移动等级需要超过十毫秒呢?听起来很复杂。你有某个项目的链接,我可以从中学习这项技术吗?
  • 是的,这很复杂;但多线程也是如此。使用多线程,您必须担心两个线程同时读取和写入相同的数据结构,您必须担心死锁和活锁以及所有这些痛苦的事情。无法避免同时做两件事很复杂的事实。

标签: c# multithreading artificial-intelligence backgroundworker threadpool


【解决方案1】:

1) 所有这些解决方案都可以使用;你只需要使用稍微不同的逻辑来处理每一个。

2) 当你有一组可以在多个线程上执行的东西时,ThreadPool 也很有用(例如,在中国跳棋游戏中,你可以通过 ThreadPool 运行 5 种不同的 AI 模拟,它会在计算机上以最佳方式运行有两个核心,而使用线程会由于上下文切换而减慢进程)。它当然适用于您的情况 - 您只需排队第二次 AIEvaluation 或其他任何东西,它就会尽快开始执行。

3) 嗯,不是真的。计算机在运行字母表(大概有一些截止深度:P)之后才能真正移动,所以无论如何人工智能线程都会完成它的工作。你可以每次都使用 ThreadPool/BackgroundWorker。

关于 BackgroundWorker 的一些一般信息:当你有“额外”的 CPU 时间时它会运行,所以如果你的主线程由于某种原因占用 CPU,它不会做很多事情。使用普通的线程池可能会更好。

假设您的程序在 AI 打开主线程时调用 AIAct()。此外,让 timerTick 成为 Windows 窗体中存在的那种计时器。此外,让 AIState 和 GameBoard 类封装 alpha-beta 所需的功能。

using System.Threading;

const int CUTOFF_DEPTH = 6;//Maximum plys for alpha-beta
AIState state;

void AIAct()
{
    state = new AIState( this.GameBoard.GetState() );
ThreadPool.QueueUserWorkItem(RunMinimax, state);

    //assume that timerTick is a Timer (Windows Forms Timer) that ticks every 100 ms
    timerTick.Enabled = true;
}

void timerTick_Tick(object sender, EventArgs e)
{
    if (state.IsComplete)
    {
        ExecuteAction(state.Result);
        timerTick.Enabled = false;
        //whatever else you need to do
    }
}

private static void RunMinimax(object args)
{
    AIState state = args as AIState;
    if (state == null)
    {
        //error handling of some sort
        Thread.CurrentThread.Abort();
    }

    //run your minimax function up to max depth of CUTOFF_DEPTH
    state.Result = Minimax( /* */ );
    state.IsComplete = true;
}

private class AIState
{
    public AIState(GameBoard board)
    {
        this.Board = board;
    }

    public readonly GameBoard Board;

    public AIAction Result;
    public volatile bool IsComplete;
}

【讨论】:

  • 使用异步委托似乎更容易,它也将在线程池中执行函数。我不明白当你有几个线程时,TreadPool 很好,但在我的情况下,我只有一个线程,为什么它有用?
  • 你有一根线,生死快,然后重生。您可以轻松地拥有一个长期存在的线程,该线程的确切职责会随着时间而变化(当您将游戏状态重新同步到它时,它会重新开始其 alpha-beta 搜索)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多