2016-03-24 3 views
1

Ich wurde mit einer Testversion von parallel computing toolbox zur Verfügung gestellt, um einige Tests zu machen und zu sehen, wie es funktioniert.Wie berichtet man Arbeiter "Ereignis" in einem Client gui

Unter den Tests, die ich durchführen wollte, ist zu sehen, wie diese Toolbox verwendet werden könnte, um einige Hintergrundverarbeitung innerhalb einer GUI auszuführen und Bericht für den Verarbeitungsfortschritt zu erstellen.

Bisher habe ich eine einfache GUI mit einer Schaltfläche zum Starten/Abbrechen der Verarbeitung im Hintergrund (unter Verwendung von parfeval) und eine Bezeichnung für den Fortschritt zu erstellen.

Alles funktioniert gut (der Code läuft im Hintergrund und ich bin in der Lage für die Hintergrundfehler oder eine Annullierung zu behandeln), das einzige Problem ist, über die Hintergrundverarbeitung Progression in Client-Sitzung berichten:

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 

Ich verstehe gut das Problem, das ist der Rückruf onBackgroundProgress wird von einer Sitzung ohne Display ausgeführt, so nichts passiert (außerdem ist es nicht mit Client-GUI synchronisiert).

Gibt es eine Möglichkeit zum Synchronisieren und Übergeben von Daten an die GUI vom Arbeiter (in C# hätte ich BeginInvoke verwendet)? Wahrscheinlich benutze ich die Toolbox nicht in angemessener Weise, um das zu erreichen, was ich gerne hätte (scheint weit mehr auf verteilte Berechnungen als auf Multithreading ausgerichtet zu sein), gibt es einen anderen Weg, dies mit dieser Toolbox zu tun? ...

EDIT

ich meinen Code drawnow mit einem timer Objekt (das funktioniert) und versuchte zu ersetzen modifizierte labSend und labReceive zu verwenden UI mit Hintergrund-Sitzung zu synchronisieren (dies nicht arbeiten):

% 
% 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 

Offenbar labSend ein labReceive nur zwischen wo auftreten kann rkers, aber nicht mit dem Kunden ... scheint eine Sackgasse zu sein.

+0

Ich bin kein PCT-Experte, aber ich denke nicht, dass das funktionieren wird; 'parfeval' führt die Funktion asynchron auf einem Pool von Arbeitern aus. Lokal läuft das auf separaten ** Prozessen ** (nicht Threads) und entfernt auf einem verteilten Cluster von Computern (denke MPI). Es ist beabsichtigt, Funktionen im Hintergrund zu starten und Ergebnisse abzurufen, sobald sie verfügbar sind (eine Seite Kommunikation), nicht ein Ersatz für Threads. Ich sehe nicht, wie Sie vom Mitarbeiter zurück zum Master-Prozess kommunizieren können, um UI-Updates zu posten. Ich kenne 'labSend' /' labReceive' Funktionen, aber ich bin mir nicht sicher, ob sie in Ihre Programmstruktur passen. – Amro

+0

Danke @Amro. Ich habe meinen Code aktualisiert und versucht, 'labSend/labReceive' zu ​​verwenden, aber es funktioniert auch nicht (siehe meinen bearbeiteten Post ... scheint' labSend/labReceive' kann nur zwischen 'workers' auftreten, aber nicht mit dem Client) ... Definitiv Die Toolbox ist weit mehr * verteilt * orientiert als orientiert *, also rate jetzt, dass es am besten ist, meinen alten Stil beizubehalten [Workaround] (http://stackoverflow.com/a/28378219/684399) GUI. – CitizenInsane

+0

Das ist der Eindruck, den ich auch habe; Das von PCT angebotene Modell eignet sich sehr gut dazu, verteilten Arbeitern "Jobs zu senden", ohne sie zu blockieren (mit Funktionen wie 'createJob',' parfeval', 'batch'), um schließlich Ergebnisse des zurückgegebenen Versprechens/verzögerten Objekts abzurufen.Ein anderer Ansatz ist der von 'parfor',' spmd' und 'mapreduce', die einen datenparallelen Berechnungsstil bereitstellen. Und schließlich gibt es CUDA- und GPU-Computing ... Alles in allem finde ich, dass MATLAB sich nicht für eine Multithread-Programmierung eignet (mit oder ohne PCT-Toolbox). – Amro

Antwort

2

Wenn Ihr Ziel nur darin besteht, den Fortschritt der Mitarbeiter zu überwachen, sollten Sie sich die parfor progress monitor v2 ansehen. Außerdem erhalten Sie eine Idee, wie Sie eine Socket-Verbindung von Workern zurück zum Client herstellen können, mit der Sie eine anspruchsvollere Benutzeroberfläche erstellen können.

+0

Ja, ich denke definitiv, dass dies bisher bei PCT der beste Ansatz für die asynchrone Instrumentierung des Clients ist. Vielen Dank :) – CitizenInsane

Verwandte Themen