2016-03-24 37 views
1

我已經提供了parallel computing toolbox的試用版以進行一些測試並查看它是如何工作的。如何在客戶端舉報工作人員「事件」gui

我想要執行的測試包括瞭解如何使用此工具箱在GUI中運行某些後臺處理,並報告處理進度。

到目前爲止,我已經創建了一個簡單的圖形用戶界面,其中包含一個用於在後臺啓動/取消處理的按鈕(使用parfeval)和一個標籤來報告進程。

,一切工作正常(代碼在後臺運行,我能夠處理後臺錯誤或取消),唯一的問題是關於客戶端會話報告後臺處理進展:

function [] = TestBackgroundWorker() 
%[ 
    % Create interface 
    fig = figure(); 
    cuo = onCleanup(@()delete(fig)); 
    stState = uicontrol('Parent', fig, 'Units', 'normalized', 'Position', [0.1 0.7 0.8 0.2], 'Style', 'text', 'String', 'Ready'); 
    btnOkCancel = uicontrol('Parent', fig, 'Units', 'normalized', 'Position', [0.1 0.1 0.8 0.5], 'Style', 'pushbutton', 'String', 'Go', 'Callback', @(s,e)onOkCancelClicked(fig)); 

    % Backstore 
    data = guidata(fig); 
    data.bgw = []; 
    data.stState = stState; 
    data.btnOkCancel = btnOkCancel; 
    guidata(fig, data); 

    waitfor(fig); 
%] 
end 
function [] = onBackgroundProgress(fig, ratio, msg) 
%[ 
    % Here I would like to 'BeginInvoke' in client thread 
    % to refresh 'ratio/msg' in the GUI. 

    % Below code of course doesn't work: 
    % 1) It is not synchronized with UI thread 
    % 2) It is executed in a session with no display 
    data = guidata(fig); 
    set(data.stState, 'String', sprintf('%f - %s', ratio, msg));  
%] 
end 
function [] = onOkCancelClicked(fig) 
%[ 
    % Backstore 
    data = guidata(fig);   
    if (~isfield(data, 'bgw')) 
     data.bgw = [];   
    end 

    if (isempty(data.bgw)) 

     % Start background work 
     set(data.btnOkCancel, 'String', 'Cancel'); 
     data.bgw = parfeval(@doBackgroundWork, 0, @(r, m)onBackgroundProgress(fig, r, m)); 
     guidata(fig, data); 

     % Wait for error/termination/cancel 
     while(true) 

      try 
       idx = fetchNext(data.bgw, 0.3); 
      catch err 

       if (~isempty(err.cause) && (strcmp(err.cause{1}.identifier, 'parallel:fevalqueue:ExecutionCancelled'))) 
        % Error was due to cancelation 
        uiwait(msgbox('Processing canceled by user!', 'modal')); 
        set(data.btnOkCancel, 'String', 'Go', 'Enable', 'on'); 
       else 
        % Error real error (TODO: display it in some way) 
        uiwait(msgbox('Processing error!', 'modal')); 
        set(data.btnOkCancel, 'String', 'Go', 'Enable', 'on'); 
       end 

       data.bgw = []; 
       guidata(fig, data); 
       break; 
      end 

      if (isempty(idx)) 
       % Still processing => Enable message pump to read GUI events 
       drawnow limitrate; 
      else 
       % Processing done 
       uiwait(msgbox('Processing done!', 'modal')); 
       data.bgw = []; 
       guidata(fig, data); 
       set(data.btnOkCancel, 'String', 'Go', 'Enable', 'on'); 
       break; 
      end 
     end 

    else 

     % Cancel background work 
     set(data.btnOkCancel, 'String', 'Cancelling...', 'Enable', 'off'); 
     cancel(data.bgw); 

    end  
%] 
end 
function [] = doBackgroundWork(onProgress) 
%[ 
    count = 10; 
    for k = 1:count,   
     onProgress((k-1)/count, sprintf('Step %i/%i', k, count)); 
     pause(1);  
    end 
%] 
end 

我明白以及這個問題,即回調onBackgroundProgress是從一個沒有顯示的會話中執行的,所以沒有任何反應(而且它沒有與客戶端GUI同步)。

有沒有一種方法來同步和傳遞數據到工作人員的GUI(在C#中我會用BeginInvoke)?也許我沒有以適當的方式使用工具箱來實現我想要的(似乎更多地面向分佈式計算而不是多線程),還有另一種方法可以用這個工具箱來實現嗎? ...

編輯

我修改代碼以timer對象來代替drawnow這個工程),並試圖用labSendlabReceive到UI背景會話同步(這不工作):

% 
% PURPOSE: 
% 
% Test function to see how to have a responsive GUI while computations 
% are running in the background. 
% 
% SYNTAX: 
% 
% [] = TestBackgroundWorker(); 
% 

%% --- Main routine 
function [] = TestBackgroundWorker() 
%[ 
    % Make sure parallel pool is started 
    gcp(); 

    % Create the interface 
    % A simple figure with a go/cancel button and a label. 
    fig = figure(); 
    cuo = onCleanup(@()delete(fig)); 
    stState = uicontrol('Parent', fig, 'Units', 'normalized', 'Position', [0.1 0.7 0.8 0.2], 'Style', 'text', 'String', 'Ready!'); 
    btnStartCancel = uicontrol('Parent', fig, 'Units', 'normalized', 'Position', [0.1 0.1 0.8 0.5], 'Style', 'pushbutton', 'String', 'Start', 'Callback', @(s,e)onOkCancelClicked(fig)); 

    % Backstore for later use 
    data = guidata(fig);  
    data.stState = stState; 
    data.btnStartCancel = btnStartCancel; 
    guidata(fig, data); 

    % Wait until figure is closed 
    waitfor(fig); 
%] 
end 

%% -- Event handler for 'go/cancel' button in the GUI 
function [] = onOkCancelClicked(fig) 
%[ 
    % Backstore 
    data = guidata(fig);   
    if (~isfield(data, 'bgw')) 
     data.bgw = [];   
    end 


    % Depending if background process is running or not 
    if (isempty(data.bgw)) 

     % Start background work 
     set(data.btnStartCancel, 'String', 'Cancel'); 
     data.bgw = parfeval(@doBackgroundWork, 0, @(r, m)onBackgroundProgress(fig, r, m)); 

     % Start timer to monitor bgw 
     data.bgwtimer = timer('ExecutionMode', 'fixedSpacing', 'Period', 0.1, ... 
           'TimerFcn', @(s,e)onBackgroundCheck(fig, data.bgw)); 

     guidata(fig, data);       
     start(data.bgwtimer); 

    else 

     % Cancel background work 
     p = gcp('nocreate'); % Make sure parpool is started 
     if (~isempty(p)) 
      cancel(data.bgw); 
     end 
     set(data.btnStartCancel, 'String', 'Cancelling (please wait)...', 'Enable', 'off'); 

    end  
%] 
end 

%% --- Event handlers for monitoring the background worker 
function [] = onBackgroundCheck(fig, bgw) 
%[  
    try 

     idx = fetchNext(bgw, 0.3); 
     if (isempty(idx)), 

      % Check for messages from the background worker 
      if ((numlabs ~= 1) && labProbe) 
       data = labReceive(); 
       onBackgroundProgress(data{:}); 
      end 

     else  
      onBackgroundCompleted(fig); 
     end 

    catch err 

     onBackgroundCompleted(fig, err); 

    end  
%] 
end 
function [] = onBackgroundCompleted(fig, err) 
%[ 
    if (nargin < 2), err = []; end 

    if (isempty(err)) 
     % Normal completion 
     uiwait(msgbox('Processing done!', 'Processing', 'help', 'modal')); 
    elseif (~isempty(err.cause) && (strcmp(err.cause{1}.identifier, 'parallel:fevalqueue:ExecutionCancelled'))) 
     % Error was due to cancelation 
     uiwait(msgbox('Processing canceled by user!', 'Processing', 'help', 'modal')); 
    else 
     % Error real error (TODO: display it in some way) 
     uiwait(msgbox(sprintf('Processing error: %s', err.message), 'Processing', 'error', 'modal'));      
    end 

    data = guidata(fig); 
    data.bgw = []; 
    stop(data.bgwtimer); 
    set(data.stState, 'String', 'Ready!'); 
    set(data.btnStartCancel, 'String', 'Start', 'Enable', 'on'); 
    guidata(fig, data);   
%] 
end 

%% --- Event handler for reporting progression status 
function [] = onBackgroundProgress(fig, ratio, msg) 
%[ 
    cw = getCurrentWorker(); 

    if (~isempty(cw)) 
     % Were are the background thread so send message to the GUI 
     % NB: Doing broadcast as I don't know the id of the gui 
     labBroadcast(labindex, {fig, ratio, msg }); 
    else 
     % Were are the GUI 
     data = guidata(fig); 
     set(data.stState, 'String', sprintf('%f - %s', ratio, msg)); 
    end  
%] 
end 

%% --- Processing to be performed in the background 
function [] = doBackgroundWork(onProgress) 
%[ 
    count = 15; 
    for k = 1:count,   
     onProgress((k-1)/count, sprintf('Step %i/%i', k, count)); 
     pause(1);  
    end 
%] 
end 

顯然labSendlabReceive可以を之間纔會發生但與客戶不在一起......似乎是一個死衚衕。

+0

我不是PCT專家,但我不認爲這會起作用; 'parfeval'在工作人員池上異步執行該功能。在本地,它在單獨的**進程**(不是線程)上運行,並在分佈式計算機集羣上遠程運行(請考慮MPI)。它打算在後臺啓動函數並獲取結果(單向通信),而不是取代線程。我沒有看到你如何能夠從工作人員返回到主進程來發布UI更新。我知道'labSend' /'labReceive'函數,但我不確定它們是否適合您的程序結構。 – Amro

+0

謝謝@Amro。我更新了我的代碼,嘗試使用'labSend/labReceive',但它也不起作用(請參閱我編輯過的帖子......似乎'labSend/labReceive'只能在'工作者'之間發生,但不會與客戶端發生)......絕對該工具箱更分散*面向而不是*多線程*面向,所以現在最好保持我的舊樣式[解決方法](http://stackoverflow.com/a/28378219/684399)有一個響應GUI。 – CitizenInsane

+0

這也是我的印象; PCT提供的模型非常適合於向分佈式工作人員「提交作業」而不會阻塞(使用像'createJob','parfeval','batch'這樣的函數),最終從返回的promise/deferred對象中檢索結果。另一種方法是提供數據並行計算風格的'parfor','spmd'和'mapreduce'。最後是CUDA和GPU計算......所有考慮事項,我發現MATLAB不適用於多線程編程風格(帶或不帶PCT工具箱)。 – Amro

回答

2

如果你唯一的目標是監測工人的進步,你可能想看看parfor progress monitor v2。它還讓您瞭解如何建立從工作人員到客戶端的套接字連接,您可以使用這種連接來構建更復雜的用戶界面。

+0

是的,我肯定認爲,迄今爲止,這是異步檢測返回客戶端的最佳方法。謝謝 :) – CitizenInsane