0. Introduction▲
Le background worker est un composant inclus dans le Framework .Net et qui permet d'exécuter
très facilement du code dans un autre thread que le thread principal de votre application
quelque soit son type. En effectuant des opérations dans un background worker on empêche ainsi
à une application d'être figée le temps de réaliser une requête à une base de données, un appel
à une ressource externe ou même des calculs complexes… Vous en conviendrez, il est très pénible
de voir une application figée lors d'un long traitement.
La solution pour éviter ce genre de désagrément consiste à exécuter le code du traitement long
à réaliser dans un autre thread que le thread principal de l'application, le Framework possède
pour cela une classe Thread. Cette classe bien que pleinement fonctionnelle n'est pas simple à
utiliser. Conscient de ce problème Microsoft a alors ajouté un composant prêt à l'emploi, le
background worker, voyons comment il fonctionne et comment l'utiliser.
I. Utilisation du background worker▲
Le background worker est un composant que l'on peut utiliser indifféremment de manière graphique par glisser/déposer sur un formulaire Windows par exemple ou alors en le déclarant directement dans le code.
BackgroundWorker bgw1 =
new
BackgroundWorker
(
);
bgw1.
DoWork +=
new
DoWorkEventHandler
(
bgw1_DoWork);
bgw1.
ProgressChanged +=
new
ProgressChangedEventHandler
(
bgw1_ProgressChanged);
bgw1.
RunWorkerCompleted +=
new
RunWorkerCompletedEventHandler
(
bgw1_RunWorkerCompleted);
Après avoir instancié le background worker il suffit d'appeler la méthode RunWorkerAsync
pour lancer le traitement asynchrone, cela signifie que le code associé est exécuté dans
un thread dédié et qu'il ne provoque donc pas un blocage ou un ralentissement de l'affichage
et des traitements du thread principal. La méthode RunWorkerAsync prend un paramètre qui
est de type object, cela laisse donc le loisir de passer ce que l'on veut au thread qui
exécutera le code. Nous verrons plus tard dans ce tutoriel comment récupérer un résultat.
Le composant background worker possède 3 évènements qui vont nous servir à placer le code
que l'on souhaite exécuter, a détecter la progression ainsi que la fin de cette exécution.
Il faut s'abonner à ces 3 évènements pour pouvoir tirer parti du composant.
II. Les évènements du background worker▲
private
void
backgroundWorker1_DoWork
(
object
sender,
DoWorkEventArgs e)
{
}
private
void
backgroundWorker1_ProgressChanged
(
object
sender,
ProgressChangedEventArgs e)
{
}
private
void
backgroundWorker1_RunWorkerCompleted
(
object
sender,
RunWorkerCompletedEventArgs e)
{
}
II-A. Disposed▲
Inutile de s'étendre sur cet évènement, vous devez simplement savoir que celui-ci est levé lorsque le composant est "disposé" suite à l'appel de la méthode Dispose().
II-B. DoWork▲
DoWork est l'évènement principal de ce composant, en effet il est levé à la suite de l'appel
de la méthode RunWorkerAsync, méthode qui permet de lancer le traitement aynschrone du worker.
Le code se trouvant dans l'event handler de l'évènement DoWork est celui qui sera exécuté dans
le thread en arrière plan, c'est donc ici que l'on doit placer le code que l'on souhaite
exécuter en tâche de fond de l'application. Voici comment s'abonner à cet évènement :
Si l'on souhaite passer un paramètre à notre worker il suffit d'utiliser la surcharge de
la méthode RunWorkerAsync ayant un paramètre de type "object". Dans l'event handler l'on
retrouver cet objet dans la propriété "Argument" du paramètre "e" qui est de type DoWorkEventArgs.
Etant donné que cette propriété est de type " object " il ne faudra pas oublier de " caster " ce
paramètre vers le type souhaité. Le DoWorkEventArgs possède en plus une propriété " Result " de
type " object " qui permet de retourner un résultat depuis le Thread en arrière plan, libre à
vous de placer ce que vous voulez dans cet objet.
Vous l'aurez compris, l'event handler de l'évènement DoWork, comme son nom l'indique, sert à
contenir le code exécuter en arrière plan, il peut prendre des paramètres en entrée et
retourner un résultat par l'intermédiaire du DoWorkEventArgs.
II-C. ProgressChanged▲
Le ProgressChanged est un évènement qui peut être levé depuis le
code exécuté en arrière plan, il permet de retourner à l'application
ayant lancé le background worker des informations sur progression du
traitement. Cet évènement pourra servir par exemple à rafraichir une
barre ou l'affichage indiquant la progression. Cet évènement peut être
désactivé par l'intermédiaire de la propriété WorkerReportsProgress en
la positionnant à False.
Pour levé cet évènement il suffit d'appeler la méthode ReportProgress
depuis le code exécutant le traitement et de passer en paramètre le
pourcentage de l'avancement (un Int32) et éventuellement un autre
paramètre de type object qui permet de retourner d'autres informations
si nécessaire, à cet effet la méthode ReportProgress propose deux signatures :
- ReportProgress(int percentProgress)
- ReportProgress(int percentProgress, object userState)
Le code se trouvant dans l'event handler de l'évènement ProgressChanged pourra alors récupérer ces informations dans les propriétiés de l'objet ProgressChangedEventArgs.
II-D. RunWorkerCompleted▲
Le troisième évènement important du background worker est le RunWorkerCompleted qui est levé automatiquement par le composant quand le traitement est terminé (sortie du event handler de l'évènement DoWork). Lorsque cet évènement est levé alors la propriété IsRunning du background worker passe à False. Comme les autres évènements celui-ci possède son propre RunWorkerCompletedEventArgs qui permet entre autre de récupérer la propriété Result de type object accessible depuis le DoWork qui permet de retourner le résultat du traitement.
III. Annulation d'un traitement en cours▲
Une fonctionnalité intéressante de ce background worker est le fait qu'il est
possible d'annuler un traitement en cours si l'on a préalablement positionné la
propriété WorkerSupportsCancellation du worker à True. En effet si un traitement
prend trop de temps ou pour n'importe quel raison vous souhaitez l'interrompre il
suffit simplement d'appeler là méthode CancelAsync(). Cependant il n'y a pas de
magie, cette méthode ne fait que positionner la propriété CancellationPending à True,
c'est alors à vous de vérifier périodiquement cette propriété dans le DoWork et de
prévoir le code de sortie si celle-ci n'est plus à False. Cela est très facile si
votre algorithme est basé sur une boucle, cela devient un peu plus délicat dans le
cas d'une connexion à une base de données par exemple, il faut alors créer le code
qui va périodiquement vérifier la propriété et libérer proprement toutes les
ressources utilisées avant de quitte le DoWork.
Attention, l'appel à la méthode CancelAsync n'a pas forcément une action immédiate
sur le positionnement de la propriété CancellationPending à True, du fait du caracètre
multi-threadé de ce composant il peut s'écouler un certain moment entre l'appel de
cette méthode et l'a modification effective.
Conclusion▲
Voilà nous en avons terminé pour cette présentation du composant Background Worker qui permet de réalisé très facilement des traitements en arrière plan de votre application, en plus les différents EventArgs et surcharges de méthodes permettent de passer des paramètres, vérifier la progression et récupérer un résultat sans difficultés. Je vous recommande la lecture du MSDN si vous souhaitez avoir plus d'informations sur ce composant, mais vous êtes désormais aptes à utiliser le background worker dans l'immense majorité des cas, n'hésitez pas à en abusez, en effet rien n'est plus frustrant de voir une application figée lors de traitement long sans pour autant savoir si l'application fonctionne toujours ou est simplement plantée.