これは、ゲームAIにとって最も推奨されるスレッドパターンであり、どのように実装するのですか?

私はC#でスレッディングするのが初めてであり、スレッディングに関する多くの理論を読んだにもかかわらず、実際にはそれほど役に立たない。

チェッカーゲームにAI関数(minmaxアルファベット)を書いて別のスレッドで実行したい。

それには4つのオプションがあります:レギュラートレッド、スレッドプール、非同期デリゲート、BackgroundWorker。

BackgroundWorkerはこれには理想的だと思うので、仕掛けを委任しているので、実際にボード上で計算された移動を行い、プログレスバーを更新する "makemove"関数を実行することができます。

私はこれについて3つの質問があります:

  1. この場合、BackgroundWorkerは本当に最適なソリューションですか?

  2. BackgroundWorkerはスレッドプールで実行されますが、そのメリットは何ですか?スレッドプールがたくさんあるときには、常にスレッドプールが良いと言われていますが、これは私の場合ではありません。

  3. 私が見たコード例はすべて単純すぎて、そのようなスレッドを1つだけ作成する方法を示しました。私のプログラムでは、コンピュータのターンのたびにこの関数を実行する必要があるので、前のスレッドを終了して新しいスレッドを開始する必要があります。このすべてを実装する正しい方法は何ですか?

どんな助けもありがとう。

1
なぜあなたはAIを自分のスレッドで動かしたいのか説明できますか? UIスレッドでAIを実行してみませんか?
追加された 著者 Eric Lippert,
AIが長時間実行されている場合のみ。 AIが10ミリ秒未満で実行されるようにAIを構築し、その場所を覚えて、フォームでイベントを処理してから10ミリ秒間繰り返し実行するのはなぜでしょうか?スレッドを追加する必要はありません。 10ミリ秒は約400万プロセッササイクルであり、確かにあなたは4千万サイクルでかなりの量の仕事をすることができます。
追加された 著者 Eric Lippert,
このテクニックの面倒な点は、(1)あなたが言っているように、あなたのパフォーマンス限界を過ぎ去っていないと判断することは、潜在的に困難であること、(2)UIメッセージを実行した後、ループは、読みにくい "裏返し"のコードを作成します。私たちはC#5の新しい "async/await"機能を使用して2番目の問題を解決しようとしています。最初の問題は解決するための問題です。
追加された 著者 Eric Lippert,
そして、はい、それは複雑です。マルチスレッドも同様です。マルチスレッドでは、同時に同じデータ構造を読み書きする2つのスレッドについて心配する必要があります。デッドロックやライブロック、そしてその苦しいものを心配する必要があります。同時に2つのことをすることは複雑であるという事実を回避することはできません。
追加された 著者 Eric Lippert,
そのため、フォームがフリーズします。
追加された 著者 geniaz1,
それについては決して考えなかった。 10種類の動きの中から1つを選択しなければならないとしましょう。それぞれを別々に計算し、いくつかのデータ構造でランクを付けます。しかし、いくつかの移動ランクを計算するのに10ミリ秒以上かかる場合はどうすればよいでしょうか?それは補完されたように聞こえる。私はこの技術を学ぶことができるいくつかのプロジェクトへのリンクを持っていますか?
追加された 著者 geniaz1,
私はC#5についてのあなたの記事を読んで、それは素晴らしい音だ!しかし、私の場合、スレッドを使うのは簡単です。
追加された 著者 geniaz1,

1 答え

1)これらのソリューションのすべておよびすべてが機能します。あなたはちょうどそれぞれを扱うために多少異なるロジックを使用しなければなりません。

2)ThreadPoolは、複数のスレッドで実行できる一連のものがある場合にも便利です(たとえば、Chinese Checkersゲームでは、ThreadPoolを使用して5つの異なるAIシミュレーションを実行することができます。スレッドを使用すると、コンテキスト切り替えのためプロセスが遅くなります)。これは確かにあなたのケースで動作します - ちょうど2番目のAIEvaluationを待ち行列に入れて、すぐに実行を開始します。

3)まあ、そうではありません。アルファベットを実行した後(おそらくいくつかのカットオフの深さでP)、コンピュータは実際には動かすことができないので、AIスレッドはどんな仕方でも実行されます。あなたは毎回ThreadPool/BackgroundWorkerを使うことができます。

BackgroundWorkerについての一般的な情報:CPU時間が余分にあるときに実行されるため、何らかの理由でメインスレッドがCPUを奪っている場合は、それほど多くはありません。通常のThreadPoolを使用する方が良いかもしれません。

AIAct()がメインスレッド上でAIのターンであるときにあなたのプログラムがAIAct()を呼び出したとしましょう。また、timerTickをWindowsフォームに存在するソートのタイマーにします。さらに、アルファベータに必要な機能をカプセル化したクラスAIStateとGameBoardを用意しましょう。

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;
}
3
追加された
あなたはすぐに生きて死ぬスレッドを持っていて、生まれ変わる。時間の経過とともに正確な任務が変更される(ゲーム状態を再同期すると、新しいアルファベータ検索が新たに開始されます)、長寿命のスレッドが1つだけ簡単に作成できます。
追加された 著者 GGulati,
代わりに、非同期デリゲートを使用するほうがずっと簡単になり、ThreadPoolで関数を実行します。私はスレッドのカップルがあるときにTreadPoolが良いと理解していますが、私の場合はスレッドが1つしかありません。
追加された 著者 geniaz1,