You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++14中使用Promise/Future获取象棋AI Minimax结果失败求助

问题分析与修复方案

核心问题解析

1. 类型不匹配引发未定义行为

代码中存在多处类型不一致:

  • task的返回类型是std::pair<Move*, float>
  • taskWorker中却将其赋值给std::pair<Move*, int>,随后调用p.set_value(bestMove),而promise的类型是std::pair<Move*, float>
    这种隐式类型转换会破坏数据结构,导致future无法正确解析promise设置的值,是f.get()无法获取结果的直接原因之一。

2. 返回局部变量指针导致悬垂指针

task中的bestMove是栈上的局部变量,你返回了&bestMove作为结果指针。当task函数返回时,bestMove会被销毁,这个指针变成悬垂指针。后续对该指针的任何操作(包括存入promise)都会触发未定义行为,可能直接导致程序崩溃或future读取异常。

3. 递归Lambda的引用捕获风险

用[&task]捕获std::function的引用,在递归调用时,这种引用捕获可能导致生命周期问题:如果task的std::function对象被销毁或重新赋值,递归调用会出现未定义行为,尤其在多线程环境下,风险更高。

4. 多线程设计的逻辑误区

当前代码只是把整个minimax递归放到单线程中执行,并没有真正实现多线程并行计算(比如拆分不同分支到多个线程),无法达到提升运算速度的目的,但这属于后续优化范畴,先解决当前的promise/future问题。


修复后的代码示例

以下是修正了上述问题的代码,主要修改点包括:

  • 移除指针返回,直接返回Move对象拷贝(用std::optional处理无走法的情况)
  • 统一所有相关类型为std::pair<std::optional<Move>, float>
  • 修正递归Lambda的捕获方式,避免引用风险
  • 修复promise/future的类型匹配问题
#include <optional>
#include <functional>
#include <thread>
#include <future>
#include <algorithm>
#include <cmath>

// 假设已有Board、Move、PossibleMoves定义,以及evaluate函数

std::pair<std::optional<Move>, float> minimax(Board& board, int depth, float alpha, float beta, bool maximizingPlayer, bool maximizingColor) {
    if (depth == 0 || board.GameIsOver()) {
        return {std::nullopt, evaluate(board, maximizingColor)};
    }

    auto possibleMoves = board.AllMovePossible(maximizingPlayer);
    std::optional<Move> bestMove;
    if (maximizingPlayer) {
        float maxEval = INFINITY;
        for (PossibleMoves& p : possibleMoves) {
            for (auto& move : p.moves) {
                board.Move(p.piece, move.first, move.second);
                auto [_, currentEval] = minimax(board, depth - 1, alpha, beta, false, maximizingColor);
                board.UndoMove();
                if (currentEval < maxEval) {
                    maxEval = currentEval;
                    bestMove = Move(p.piece, move);
                }
                beta = std::min(beta, currentEval);
                if (beta <= alpha)
                    break;
            }
        }
        return {bestMove, maxEval};
    } else {
        float maxEval = -INFINITY;
        for (PossibleMoves& p : possibleMoves) {
            for (auto& move : p.moves) {
                board.Move(p.piece, move.first, move.second);
                auto [_, currentEval] = minimax(board, depth - 1, alpha, beta, true, maximizingColor);
                board.UndoMove();
                if (currentEval > maxEval) {
                    maxEval = currentEval;
                    bestMove = Move(p.piece, move);
                }
                alpha = std::max(alpha, currentEval);
                if (beta <= alpha)
                    break;
            }
        }
        return {bestMove, maxEval};
    }
}

std::pair<std::optional<Move>, float> runMinimaxInThread(Board board, int depth, float alpha, float beta, bool maximizingPlayer, bool maximizingColor) {
    std::promise<std::pair<std::optional<Move>, float>> p;
    auto f = p.get_future();

    std::thread worker([&](std::promise<std::pair<std::optional<Move>, float>>&& promise) {
        auto result = minimax(board, depth, alpha, beta, maximizingPlayer, maximizingColor);
        promise.set_value(result);
    }, std::move(p));

    auto bestMoveFromThread = f.get();
    worker.join();

    return bestMoveFromThread;
}

关键修改说明

  1. 替换指针返回为std::optional<Move>:避免悬垂指针问题,std::nullopt用于表示没有最优走法(比如搜索深度为0时)。
  2. 统一类型:所有涉及结果的类型都统一为std::pair<std::optional<Move>, float>,彻底消除类型不匹配问题。
  3. 将递归逻辑封装为普通函数:避免Lambda递归的引用捕获风险,代码更清晰易维护。
  4. 线程Lambda的安全传递:通过移动语义传递promise,确保线程内拥有promise的所有权,避免生命周期问题。

内容的提问来源于stack exchange,提问作者Benjamin M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 15:15:42