主要内容

本页采用了机器翻译。点击此处可查看最新英文版本。

使用 parfeval 玩 21 点游戏

此示例使用 Parallel Computing Toolbox™ 玩一种叫做 blackjack(也称为 21 点)的纸牌游戏。我们仿真了许多玩家同时独立玩数千手牌,并显示赔付统计数据。此示例使用 parfeval 在并行工作单元池上异步运行仿真。通过这种方式,我们可以在结果可用时更新其显示。

相关示例:

您可以在函数中找到此示例中显示的代码:

function paralleldemo_blackjack_parfeval

分析顺序问题

由于二十一点玩家彼此独立,我们可以并行仿真他们。我们通过将问题划分为多个函数评估来实现这一点。我们最多运行 numPlayers 次仿真,其中每个玩家玩 numHands 手二十一点。我们会在结果可用时立即绘制结果图,如果经过的时间超过 maxSimulationTime 秒或用户取消执行,我们就会终止仿真。

numPlayers        = 100;
numHands          = 5000;
maxSimulationTime = 20;

将工作划分为单独的函数评估

我们调用 parfeval 函数来请求对并行池工作单元的仿真进行评估。如果有必要,并行池将自动创建。parfeval 函数返回一个 parallel.Future 对象,当结果可用时,我们可以使用该对象来访问结果。您可以查看 pctdemo_task_blackjack 的代码以了解完整详情。

for idx = numPlayers:-1:1
    futures(idx) = parfeval(@pctdemo_task_blackjack, 1, numHands, 1);
end
% Create an onCleanup to ensure we do not leave any futures running when we exit
% this example.
cancelFutures = onCleanup(@() cancel(futures));

为收集结果和监控进度而设立

并行池工作单元立即开始运行 pctdemo_task_blackjack,我们可以使用 fetchNext 方法立即收集并显示可用结果。我们使用 resultsSoFar 来累积结果。我们更新数组 completed 来指示 futures 的哪些元素已经完成,并增加计数器 numCompleted。我们为 timeout 方法提供可选参量 fetchNext,以便在没有新结果时它能够快速返回。

resultsSoFar = zeros(numHands, numPlayers); % Allocate space for all results
completed    = false(1, numPlayers);        % Has a given future completed yet
timeout      = 2;                           % fetchNext timeout in seconds
numCompleted = 0;                           % How many simulations have completed
fig          = pctdemo_setup_blackjack(1);  % Create a figure to display results

% Build a waitbar with a cancel button, using appdata to track
% whether the cancel button has been pressed.
hWaitBar = waitbar(0, 'Blackjack progress', 'CreateCancelBtn', ...
                   @(src, event) setappdata(gcbf(), 'Cancelled', true));
setappdata(hWaitBar, 'Cancelled', false);

收集并显示可用的结果

我们通过循环调用 fetchNext 来收集和显示结果,直到我们看到 numPlayers 的结果。当 fetchNext 返回新的结果时,我们将结果分配到 resultsSoFar 中,更新 completed 数组和 numCompleted 计数器,并更新图。如果用户按下等待栏上的取消按钮或者 maxSimulationTime 过期,我们将提前中止循环。

startTime = clock();
while numCompleted < numPlayers

    % fetchNext blocks execution until one element of futures has completed.  It
    % then returns the index into futures of the element that has now completed,
    % and the results from execution.
    [completedIdx, resultThisTime] = fetchNext(futures, timeout);

    % If fetchNext timed out returning an empty completedIdx, do not attempt to
    % process results.
    if ~isempty(completedIdx)
        numCompleted = numCompleted + 1;
        % Update list of completed futures.
        completed(completedIdx) = true;
        % Fill out portion of results.
        resultsSoFar(:, completedIdx) = resultThisTime;
        % Update plot.
        pctdemo_plot_blackjack(fig, resultsSoFar(:, completed), false);
    end

    % Check to see if we have run out of time.
    timeElapsed = etime(clock(), startTime);
    if timeElapsed > maxSimulationTime
        fprintf('Simulation terminating: maxSimulationTime exceeded.\n');
        break;
    end

    % Check to see if the cancel button was pressed.
    if getappdata(hWaitBar, 'Cancelled')
        fprintf('Simulation cancelled.\n');
        break;
    end

    % Update the waitbar.
    fractionTimeElapsed      = timeElapsed / maxSimulationTime;
    fractionPlayersCompleted = numCompleted / numPlayers;
    fractionComplete         = max(fractionTimeElapsed, fractionPlayersCompleted);
    waitbar(fractionComplete, hWaitBar);
end
fprintf('Number of simulations completed: %d\n', numCompleted);

% Now the simulation is complete, we can cancel the futures and delete
% the waitbar.
cancel(futures);
delete(hWaitBar);
Simulation terminating: maxSimulationTime exceeded.
Number of simulations completed: 74

end