首页 > 解决方案 > 如何在 MATLAB 中为并行外部进程创建异步等待栏?

问题描述

我需要等待一个过程以基本上未知的持续时间完成。我通过以下方式调用此过程:

output = system('foo.cmd')

进程foo正在阻止任何进一步的代码执行。该过程通常需要大约 10 秒才能完成,并且不会返回任何状态,但之后会返回其输出值。有时需要一秒钟,有时需要一分钟或更长时间。为了模拟用户的进度,我想在执行之前实现一个等待栏,有点像:

function f = my_waitbar()
    f = waitbar(0,'Please wait...');

    for i=1:10
    pause(1)
        waitbar(i/10,f,'Executing foo');
    end
    waitbar(1,f,'Finishing, please wait...'); % should wait now and do nothing
end

然后foo.cmd完成后我会关闭酒吧。

f = my_waitbar() % blocks my code from execution, therefore is pointless
output = system('foo.cmd')
close(f)

但是仅仅通过提前调用函数f = my_waitbar(),该过程不会被并行触发。如何在my_waitbar()不等待完成的情况下执行?

请注意,之后的一切system('foo.cmd')都取决于它的执行,因此进一步的代码执行必须像现在一样等待它,所以system('foo.cmd &')对我来说效果不佳。

编辑

请假设我对内容没有影响foo.cmd

标签: matlab

解决方案


正如 Wolfie 已经提到的,标准进度条不适合等待具有未知持续时间(或未知迭代)的进程。在这些情况下,您最好使用微调器(gif 文件)、循环等待栏(文件交换中的好选择:cProgress)或无限进度条。这是我在这个例子中使用的:

在此处输入图像描述


现在如何使它成为可能。由于我无法复制您的确切过程 foo.cmd,因此我将其替换为 dos 命令dir。因此,对于基线示例:

tic
command = 'dir' ;
[~,cmdout] = system(command) ;
toc

>> Elapsed time is 1.547987 seconds.

1.5 秒足以引起注意,并且cmdout确实包含文件和目录的列表。所以我假设这与你的情况一样接近。

为了能够监控流程的结束,我将调用dir(或在您的情况下调用foo.cmd)打包到一个批处理文件中,该文件将:

  • 检查文件 ( "MyProcessIsFinished") 是否存在。如果是,请删除它。
  • 调用感兴趣的进程,将输出重定向到文件。
  • 创建一个空文件"MyProcessIsFinished"

这允许 MATLAB 调用批处理文件而无需等待结果(使用&)。然后让 MATLAB 等到它检测到文件。检测到时,您知道该过程已完成,您可以关闭等待栏并继续执行代码。然后,您可以读取包含您用于获取的结果的文件cmdout

% initialise flag
processFinished = false ; 

% start the continuous waitbar
hw = mywaitbar(0.5,'Please wait','Waiting for response ...',true);

% call the process in the batch file, without waiting for result
command = 'mycommand.bat &' ;
system(command) ;

% wait for the process to be finished
while ~processFinished
    if exist('MyProcessIsFinished','file')
        processFinished = true ;
    end
end
close(hw) ; % close the wait bar

% now read your results
cmdout = fileread('outputfile.txt') ;

该文件mycommand.bat现在是:

@echo off
if exist MyProcessIsFinished del MyProcessIsFinished
dir > outputfile.txt
copy nul MyProcessIsFinished > nul
exit

不要忘记dir > outputfile.txt通过调用您的进程并重定向到合适的文件名来替换该行。它可能看起来像:

foo.cmd > ReceivedRequest.json

mywaitbar.m连续的等待栏:我从文件交换中拿起: mywaitbar。代码很好,但我不得不改变一些东西来改进计时器管理,所以如果你想要一个工作版本,会有一些改变:

  • 第 109 行:添加'CloseRequestFcn',@closeRequestFcn到新图形的属性。
  • 第 120 行:添加'Name','CircularWaitbarTimer'到新计时器的属性中

然后在文件的底部,添加以下函数:

function closeRequestFcn(hobj,~)
    % Delete the timer
    t = timerfindall('Name','CircularWaitbarTimer') ;
    if strcmpi('on',t.Running)
        stop(t) ;
    end
    delete(t)
    delete(hobj)

这将使等待栏实用程序更加稳定,并消除有关未正确管理的计时器的烦人警告/错误消息。


推荐阅读