4 votes

BackgroundWorker - Passage de CancellationPending à false dans RunWorkerCompleted. Pourquoi ?

Après avoir annulé le BackGroundWorker, dans le DoWork, le CancellationPending est vrai mais quand il arrive au RunWorkerCompleted, le CancellationPending est faux. Je ne sais pas ce que j'ai fait de mal ?

static BackgroundWorker b1;

static void Main(string[] args)
{
    b1=new BackgroundWorker();
    b1.DoWork += new DoWorkEventHandler(work1);
    b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
    b1.WorkerSupportsCancellation = true;
    b1.RunWorkerAsync("Hellow");
    Console.ReadLine();
}

private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
    if (((BackgroundWorker)sender).CancellationPending)
        Console.WriteLine("Canceled!");
    else
        Console.WriteLine("Result:" + e.Result);//it goes here every time
}

private static void work1(object sender, DoWorkEventArgs e)
{
    ((BackgroundWorker)sender).CancelAsync();
    if (((BackgroundWorker)sender).CancellationPending)
    {
        e.Cancel = true;
    }
}

A propos, comment puis-je ajouter une erreur qui se produit dans le DoWork à RunWorkerCompletedEventArgs.Error pour la montrer à l'utilisateur ?

8voto

Hans Passant Points 475940

Oui, la classe BackgroundWorker définit la propriété CancellationPending sur false avant de déclencher l'événement RunWorkerCompleted. Si le travailleur a été effectivement annulé ou non.

C'est tout à fait intentionnel, cela vous empêche de tomber dans un piège désagréable qui est toujours présent lorsque vous utilisez des fils. Le code qui utilise des threads se comporte souvent de manière aléatoire et imprévisible en raison d'un type de bogue appelé "threading race". Il s'agit d'un type de bogue très courant et terriblement difficile à déboguer.

Ce qui peut facilement aller mal dans votre approche prévue si BGW n'a pas fait cela, c'est que vous allez supposer que le travailleur a été annulé lorsque vous voyez CancellationPending défini à true. Mais c'est une illusion, vous ne pouvez pas faire la différence entre l'annulation et l'achèvement normal. Le cas extrême est celui où vous appelez CancelAsync() une microseconde avant que le worker ne se termine. Le travailleur n'a même pas la chance de voir le drapeau CancellationPending activé, car il était occupé à terminer les derniers détails de la méthode du gestionnaire d'événement DoWork. C'est une course de threads, le travailleur a devancé votre appel et a terminé normalement.

Pour éviter ce bogue, le travailleur doit définir e.Cancel comme vrai lorsqu'il voit la propriété CancellationPending définie comme vraie. Et bien sûr, arrêtez ce qu'il est en train de faire. Maintenant, c'est fiable, la propriété e.Cancelled dans le gestionnaire d'événement RunWorkerCompleted est une copie de e.Cancel. Votre code peut donc maintenant vous dire de manière fiable si le travailleur a vu ou non la demande d'annulation.

6voto

Brian S Points 2946

Je pense que la propriété CancellationPending est à utiliser pendant l'opération en arrière-plan (dans votre méthode work1). Elle indique au travailleur d'arrière-plan que vous avez demandé l'annulation de l'opération d'arrière-plan. Une fois que l'événement RunWorkerCompleted est appelé, le travailleur d'arrière-plan a effectué le travail d'annulation de la demande, et l'annulation n'est donc plus en attente.

EDIT : le RunWorkerCompletedEventArgs possède une propriété Cancelled qui vous indique si l'opération en arrière-plan a été annulée.

Si vous lancez une exception à partir de la méthode DoWork (work1 dans votre cas), elle doit être attrapée par le BackgroundWorker et remplir la propriété Error des RunWorkerCompletedEventArgs.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X