J'ai une application C# WPF utilisant une approche MVVM plutôt noddy. Dans l'un des ViewModels, je voudrais exécuter une séquence de tâches de manière séquentielle, mais souhaiterais que chaque tâche soit asynchrone au thread principal. Je veux la granularité d'être en mesure de signaler les progrès entre les tâches, mais je ne veux pas bloquer l'interface graphique tant que l'une des tâches est en cours d'exécution.Tâches asynchrones séquentielles sans blocage
Existe-t-il un moyen standard d'y parvenir, ou une «meilleure pratique»?
J'ai mis en place quelque chose qui utilise BackgroundWorker
que je sens à la fois heureux et légèrement horrifié avec. Le code pour donner un coup de pied au tout se sent surtout non-C# ish. Je pense qu'il doit y avoir une meilleure ou, au moins, une façon établie de le faire.
Un grand merci pour vos suggestions.
Dan
Voici l'option bricolé:
protected void runAsyncTask(SequentialTask seqTask)
{
if (HasErrored) return;
DoWorkEventHandler worker = (s, e) =>
{
setTaskStartStatusMessage(seqTask.TaskMessage);
ShowProgress = true;
seqTask.Task((BackgroundWorker)s);
};
ProgressChangedEventHandler progress = (s, e) =>
{
if (seqTask.TaskProgress != null)
seqTask.TaskProgress(e.ProgressPercentage, e.UserState);
};
RunWorkerCompletedEventHandler done = null;
done = (s, e) =>
{
ShowProgress = false;
if (e.Error != null)
{
HasErrored = true;
displayTaskExceptionMessage(e.Error, seqTask.TaskMessage);
}
else
{
setTaskCompleteStatusMessage(seqTask.TaskMessage);
if (seqTask.TaskComplete != null)
seqTask.TaskComplete();
}
((BackgroundWorker)s).RunWorkerCompleted -= done;
((BackgroundWorker)s).DoWork -= worker;
((BackgroundWorker)s).ProgressChanged -= progress;
if (seqTask.NextTask != null && (seqTask.CanExecuteNext == null ? true : seqTask.CanExecuteNext()))
runAsyncTask(seqTask.NextTask);
};
if (seqTask.TaskProgress != null)
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += worker;
backgroundWorker.RunWorkerCompleted += done;
backgroundWorker.ProgressChanged += progress;
backgroundWorker.RunWorkerAsync();
}
SequentialTask
est juste un simple ensemble de propriétés:
public class SequentialTask
{
public Action<BackgroundWorker> Task { get; set; }
public String TaskMessage { get; set; }
public Func<bool> CanExecuteNext { get; set; }
public Action<int, object> TaskProgress { get; set; }
public Action TaskComplete { get; set; }
public SequentialTask NextTask { get; set; }
}
Ce qui conduit à la syntaxe Perl comme de:
runAsyncTask(new SequentialTask()
{
Task = (x) => loadFile(),
TaskMessage = "Load File",
CanExecuteNext = null,
NextTask = new SequentialTask()
{
Task = (x) => validateImport(),
TaskMessage = "Validate Input Lines",
TaskComplete =() =>
{
if (!ImportIsValid)
displayValidationMessages();
},
CanExecuteNext =() => ImportIsValid,
NextTask = new SequentialTask()
{
etc.
Coroutines ou Reactive Framework sont un bon ajustement pour cela: http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-in.html – Jay
Salut Jay, merci pour le lien - a jeté un coup d'oeil Et c'est quelque chose que je vais certainement passer le temps à faire tourner ma tête mais je pense que la suggestion d'Adam sur le TPL est là où je vais passer mes efforts initiaux. –