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; }
关键修改说明
- 替换指针返回为
std::optional<Move>:避免悬垂指针问题,std::nullopt用于表示没有最优走法(比如搜索深度为0时)。 - 统一类型:所有涉及结果的类型都统一为
std::pair<std::optional<Move>, float>,彻底消除类型不匹配问题。 - 将递归逻辑封装为普通函数:避免Lambda递归的引用捕获风险,代码更清晰易维护。
- 线程Lambda的安全传递:通过移动语义传递
promise,确保线程内拥有promise的所有权,避免生命周期问题。
内容的提问来源于stack exchange,提问作者Benjamin M
相关产品推荐
相关产品推荐

