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