BackgroundWorker et Dispatcher

De Banane Atomic
Révision datée du 7 juin 2018 à 15:18 par Nicolas (discussion | contributions)
(diff) ← Version précédente | Voir la version actuelle (diff) | Version suivante → (diff)
Aller à la navigationAller à la recherche

Principe

BackgroundWorker permet d’exécuter du code de manière synchrone sur un thread en tache de fond.
On créé donc un second thread pour exécuter du code afin de ne pas bloquer le thread principal.
Approche différente de async, qui ne créé pas de nouveau thread mais met en pause et en mémoire le code à exécuter de manière asynchrone et l'exécute par petit bout afin de ne pas bloquer le thread principal.

The async-based approach to asynchronous programming is better than BackgroundWorker for IO-bound operations because the code is simpler and you don't have to guard against race conditions.

In combination with Task.Run, async programming is better than BackgroundWorker for CPU-bound operations because async programming separates the coordination details of running your code from the work that Task.Run transfers to the threadpool.

https://msdn.microsoft.com/en-us/library/mt674882.aspx

BackgroundWorker et Dispatcher

Le Dispatcher permet de modifier des éléments (graphiques) du thread principal (MainThread) depuis un autre thread (background thread).

Le Dispatcher n'est accessible que depuis la couche vue.
Csharp.svg
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += new DoWorkEventHandler(backgroundWorker_DoWork);
backgroundWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker_RunWorkerCompleted);

// Lance le thread en tâche de fond
backgroundWorker.RunWorkerAsync();

// Méthode qui contient le code a éxécuter dans un thread secondaire
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // le Dispatcher permet de modifier des éléments graphiques
    // depuis un thread autre que le thread graphique.
    Dispatcher.Invoke(DispatcherPriority.Normal, new Action<TypeArgument1, TypeArgument2>(méthode, argument1, argument2);
    // Avec un paramètre de retour
    bool b = (bool)System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, new Func<bool>(MaMethode));

    // dans le cas ou le BackgroundWorker serait static
    // il faut appeler le Dispatcher d'un objet non-static,
    // ici le Dispatcher du formulaire.
    mainWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
    {
        mainWindow.LoadGantt(schedule);
    }));   
}

// Méthode appelée lorsque le work a été réalisé
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // si une Exception s'est produit durant le DoWork,
    // alors elle sera stockée dans e.Error
    if (e.Error != null)
    {
        MessageBox.Show(e.Error.Message, "Error", MessageBoxButton.OK,
            MessageBoxImage.Error, MessageBoxResult.OK, MessageBoxOptions.None);
    }
    else
    {
        // n'acceder à e.Result que si e.Error est null
    }
}
An object reference is required for the non-static field, method, or property.
Utiliser System.Windows.Application.Current.Dispatcher.Invoke

Passer des arguments au BackgroundWorker

Csharp.svg
var backgroundWorker = new BackgroundWorker();
backgroundWorker.DoWork += backgroundWorker_DoWork;

var monArgument = 10;
// Lance le thread en tâche de fond
backgroundWorker.RunWorkerAsync(monArgument); // un seul argument de type object peut être passé

void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
    // récupération de l'argument
    int argument = (int)e.Argument;
}


// un autre moyen de passer des arguments
backgroundWorker.DoWork += (obj, e) => WorkerDoWork(arg1, arg2);

void WorkerDoWork(string arg1, int arg2) { }

Callback

Cs.svg
public delegate void Callback();

public void MaMethode()
{
    // test si le thread en cours peut accéder aux ressources de l'objet courant
    if (this.Dispatcher.CheckAccess())
    { // modification des ressources de l'objet courant }
    else
    { // relancer la méthode avec le bon thread
        var myCallback = new Callback(MaMethode);
        this.Dispatcher.Invoke(DispatcherPriority.Normal, myCallback);
    }
}