2009-05-05 9 views
4

C# 2008C# Annulation du travail du fondeur

J'utilise le code ci-dessous pour me connecter à un softphone. Cependant, le processus de connexion est un processus long car il y a beaucoup de choses qui doivent être initialisées et vérifiées, je n'en ai mis que quelques-unes ici, car cela rendrait le code long à poster.

Dans le code ci-dessous, je vérifie si la fonction CancellationPending si CancelAsync a été appelée dans mon événement de clic sur le bouton Annuler, avant chaque vérification. Est-ce correct? De plus, si la vérification échoue, j'appelle aussi CancelAsync et place l'e.Cancel à true.

Je voudrais savoir si ma méthode que j'ai utilisée ici est la meilleure méthode à utiliser.

Un grand merci pour tous les conseils,

private void bgwProcessLogin_DoWork(object sender, DoWorkEventArgs e) 
    { 
     /* 
     * Perform at test to see if the background worker has been 
     * cancelled by the user before attemping to continue to login. 
     * 
     * Cancel background worker on any failed attemp to login 
     */ 

     // Start with cancel being false as to reset this if cancel has been set to true 
     // in the cancel button. 
     e.Cancel = false; 

     NetworkingTest connection_test = new NetworkingTest(); 
     if (!this.bgwProcessLogin.CancellationPending) 
     { 
      // Check local LAN or Wireless connection    
      if (!connection_test.IsNetworkConnected()) 
      { 
       // Update label 
       if (this.lblRegistering.InvokeRequired) 
       { 
        this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "No network connection"); 
       } 
       else 
       { 
        this.lblRegistering.Text = "No network connection"; 
       } 
       // Failed attemp 
       this.bgwProcessLogin.CancelAsync(); 
       e.Cancel = true; 
       return; 
      } 
      // Report current progress 
      this.bgwProcessLogin.ReportProgress(0, "Network connected"); 
     } 
     else 
     { 
      // User cancelled 
      e.Cancel = true; 
      return; 
     } 

     // Test if access to Server is available 
     if (!this.bgwProcessLogin.CancellationPending) 
     { 
      if (!connection_test.IsSIPServerAvailable()) 
      { 
       // Update label 
       if (this.lblRegistering.InvokeRequired) 
       { 
        this.lblRegistering.Invoke(new UpdateRegisterLabelDelegate(UpdateRegisterLabel), "Server unavailable"); 
       } 
       else 
       { 
        this.lblRegistering.Text = "Server unavailable"; 
       } 
       // Failed attemp 
       this.bgwProcessLogin.CancelAsync(); 
       e.Cancel = true; 
       return; 
      } 
      // Report current progress 
      this.bgwProcessLogin.ReportProgress(1, "Server available"); 
     } 
     else 
     { 
      // User cancelled 
      e.Cancel = true; 
      return; 
     } 
     . 
     . 
     . 
} 


private void bgwProcessLogin_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     // Check for any errors 
     if (e.Error == null) 
     { 
      if (e.Cancelled) 
      { 
       // User cancelled login or login failed     
      } 
      else 
      { 
       // Login completed successfully     
      } 
     } 
     else 
     { 
      // Something failed display error 
      this.statusDisplay1.CallStatus = e.Error.Message; 
     } 
    } 


private void bgwProcessLogin_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     this.lblRegistering.Text = e.UserState.ToString(); 
    } 

private void btnCancel_Click(object sender, EventArgs e) 
    { 
     // Cancel the logging in process 
     this.bgwProcessLogin.CancelAsync(); 
     this.lblRegistering.Text = "Logged out"; 
} 

Répondre

8

Il y a peut-être un seul problème: si l'une des opérations du gestionnaire d'événements DoWork dure longtemps. Dans ce cas, vous pouvez annuler votre opération en attente seulement après la fin de l'opération. Si toutes les opérations dans l'événement DoWork ne peuvent pas durer très longtemps (par exemple, pas plus de 5 secondes), tout est OK, mais si l'une des opérations peut durer longtemps (5 minutes, par exemple), dans ce cas l'utilisateur a attendre que cette opération soit terminée.

Si DoWork contient des opérations de longue durée, vous pouvez utiliser quelque chose comme AbortableBackgroundWorker. Quelque chose comme ceci:

public class AbortableBackgroundWorker : BackgroundWorker 
{ 
    private Thread workerThread; 

    protected override void OnDoWork(DoWorkEventArgs e) 
    { 
     workerThread = Thread.CurrentThread; 
     try 
     { 
      base.OnDoWork(e); 
     } 
     catch (ThreadAbortException) 
     { 
      e.Cancel = true; //We must set Cancel property to true! 
      Thread.ResetAbort(); //Prevents ThreadAbortException propagation 
     } 
    } 


    public void Abort() 
    { 
     if (workerThread != null) 
     { 
      workerThread.Abort(); 
      workerThread = null; 
     } 
    } 
} 

Dans ce cas, vous pouvez vraiment interrompre les opérations en cours, mais vous avez également des restrictions (pour plus d'informations sur thread managé et l'abandon des restrictions voir Plumbing the Depths of the ThreadAbortException Using Rotor).

P.S. Je suis d'accord avec Oliver que vous devriez envelopper InvokeRequired sous une forme plus utilisable.

+0

Bonne réponse. J'espérais que ça marcherait aussi dans Silverlight. Il s'avère que ce n'est pas à cause d'une restriction de sécurité. L'appel 'Thread.Abort()' va lancer une exception MethodAccessException à partir de Silverlight 4 (http://msdn.microsoft.com/en-us/library/ty8d3wta(v=VS.95).aspx). Eh bien, c'est toujours une bonne réponse. –

+2

@SergeyTeplyakov hi Sergey, j'ai exactement le même scénario (un thread long que je veux abandonner si on clique sur cancel) mais je ne vois pas comment l'annuler de votre code/post. Je suis nouveau pour les travailleurs de fond, alors pardonnez mon ignorance ... – ganders

1

Vous faites de la bonne façon, je crois. Vous trouverez des membres de threads qui vous permettent de terminer ou d'abandonner un thread, mais vous ne voulez pas les utiliser pour quelque chose comme ça. Il peut sembler un peu bizarre d'avoir tous les contrôles "annulés" dans votre code, mais cela vous permet de contrôler exactement quand vous quittez votre thread. Si vous deviez "brutalement" annuler le thread de travail, le thread n'a aucun contrôle sur sa sortie et il peut y avoir un état corrompu.

0

Il est une chose que je n'ai pas besoin d'appeler le this.bgwProcessLogin.CancelAsync(); comme vous pouvez juste définir ce e.Cancel = true;

1

Dans votre fonction DoWork(), vous avez écrit .... En fonction du nombre de tâches de la même structure qui arrivent comme les deux affichées, vous pouvez refactoriser cette structure en une méthode propre, en donnant les parties changeantes en tant que paramètres.

Cette branche if-else de InvokeRequired a également doublé la chaîne de sortie. Une petite recherche ici sur stackoverflow ou sur le web devrait vous montrer un schéma pour accomplir ce doublage.

Tout le reste semble assez bon.

Questions connexes