2010-10-08 6 views
0

Voici mon scénario: Sur un formulaire j'ai une liste de direcotories, bouton et contrôle pour afficher le texte multiligne. En boucle, j'essaie de trouver tous les fichiers de chaque répertoire et de les supprimer. Lorsque le fichier est supprimé, je souhaite ajouter du texte au contrôle multiligne. Mon problème est que lorsque du texte est ajouté, je ne peux rien faire d'autre. Le formulaire est bloqué et si j'essaie de le faire, il cesse de répondre. Les fichiers sont supprimés à l'aide BackgroundWorkerFonctionnement en arrière-plan et blocage de la forme principale

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
    { 
//this is datatable with directories and other info 
     MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE = e.Argument as MainDataset.CZYSZCZENIEDataTable; 
     CzyscPliki(CZYSZCZENIE, ReportProgress); 
    } 

private void CzyscPliki(MainDataset.CZYSZCZENIEDataTable CZYSZCZENIE, ReportProgressDel del) 
    { 
     DirectoryInfo dir = null; 
     FileInfo[] files = null; 
     bool subfolder = false; 
     string katalog = ""; 
     string maska = ""; 
     string[] maski = null; 
     long total=0; 

     string dirS; 
     string fileS; 
     long fileLen; 
//foreach directory to delete 
     foreach (DataRow r in CZYSZCZENIE.Rows) 
     { 
//CanRead - check if row is not deleted or detached 
//r["CZYSC"].AsBool() - check if directory should be cleared 
      if (r.CanRead() && r["CZYSC"].AsBool()) 
      { 
       subfolder = r["PODKATALOGI"].AsBool(); 
       katalog = r["KATALOG"].AsString().TrimEnd('\\'); 
       maska = r["MASKA"].AsString(); 
       if (maska.IsEmpty()) 
        maska = "*"; 
       maski = maska.Split(';'); 
       dir = new DirectoryInfo(katalog); 
       if (dir.Exists) 
       { 
        foreach (string s in maski) 
        { 
         files = dir.GetFiles(s, (subfolder ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly)); 
         dir.GetFiles(); 
         foreach (FileInfo f in files) 
         { 
          dirS = f.Directory.FullName; 
          fileS = f.Name; 
          fileLen = f.Length; 
          try 
          { 
           f.Delete(); 
           total += fileLen; 
           if (del != null) 
//here is problem: del - delegate to report state 
//when it is called it blocks form 
            del(dirS, fileS, fileLen, total); 

          } 
          catch (Exception ex) 
          { } 
         } 
        } 
       } 
      } 
     } 
    } 
//this is the delegate that appends text in multiline control 
//memoEdit1 is the control 
//ceReportProgress.Checked - check if report should be added 
private void ReportProgress(string directory, string file, long size, long totalSize) 
    { 
     if (memoEdit1.InvokeRequired) 
     { 
      memoEdit1.BeginInvoke(new Action<string, string, long, long>(ReportProgress), directory, file, size, totalSize); 
     } 
     else 
     { 
      if (ceReportProgress.Checked) 
      { 
       if (file.IsEmpty()) 
        memoEdit1.AppendText("\r\nCzyszczenie katalogu " + directory); 
       else 
       { 
        memoEdit1.AppendText(file); 
        if (size > 0) 
        { 
         if (size > 1048576) 
         { 
          decimal d = size/1048576; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("\tWielkość : " + d.AsString() + " megabajtów", false); 
         } 
         else if (size > 1024) 
         { 
          decimal d = (decimal)size/(decimal)1024; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("\tWielkość : " + d.AsString() + " kilobajtów", false); 
         } 
         else 
          memoEdit1.AppendText("\tWielkość : " + size.AsString() + " bajtów", false); 
        } 
        if (totalSize > 0) 
        { 
         if (totalSize > 1073741824) 
         { 
          decimal d = (decimal)totalSize/(decimal)1073741824; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " gigabajtów"); 
         } 
         else if (totalSize > 1048576) 
         { 
          decimal d = (decimal)totalSize/(decimal)1048576; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " megabajtów"); 
         } 
         else if (totalSize > 1024) 
         { 
          decimal d = (decimal)totalSize/(decimal)1024; 
          d = decimal.Round(d, 2); 
          memoEdit1.AppendText("Zwolniono dotychczas : " + d.AsString() + " kilobajtów"); 
         } 
         else 
          memoEdit1.AppendText("Zwolniono dotychczas : " + totalSize.AsString() + " bajtów"); 
        } 
       } 
//scroll to the end of control 
       memoEdit1.ScrollToEnd(); 
      } 
     } 
    } 

Comment puis-je améliorer cela pour le rendre ne bloque pas la forme?

+0

Je ne vois pas où vous instanciez votre travailleur. Appelez-vous 'RunWorkerAsync();'? –

+0

J'appelle backgroundWorker1.RunWorkerAsync (GetRowsToDelete()); lorsque l'utilisateur appuie sur le bouton GetRowsToDelete() renvoie la table avec les répertoires à supprimer. BackgroundWorker fonctionne correctement et supprime les fichiers, mais l'affichage des informations bloque mon formulaire – Pawel

+0

Rompre dans le débogueur et voir où les threads bloquent.Y a-t-il un gestionnaire souscrit à l'événement TextChanged de memoEdit1? – Henrik

Répondre

2

Vous appelez trop souvent ReportProgress. Faites-le plus d'environ 1000 fois par seconde et le fil de l'interface utilisateur est inondé de demandes qu'il ne peut pas suivre. Il ne se déplacera pas pour faire ses devoirs normaux, qui incluent peindre les commandes et répondre à la souris et au clavier. Il a l'air gelé. Cela devient pire lorsque le code de mise à jour de l'interface utilisateur devient plus cher, la mise à jour du texte dans un TextBox quand il y a déjà beaucoup de texte peut être assez lente. Le diagnostic voit encore l'interface utilisateur figée pendant un certain temps après l'arrêt du fonctionnement de BGW, travaillant à vider le backlog dans la file d'attente des demandes d'invocation, puis revenant soudainement en vie lorsque la file d'attente est finalement vidée.

Vous devez limiter la vitesse à laquelle vous appelez BeginInvoke(). Il n'est jamais plus logique de l'appeler plus d'une fois toutes les 50 millisecondes, un humain ne peut pas percevoir la différence au-delà de cela. Collectez les informations dans une liste <> afin de pouvoir commencer BeginInvoke() beaucoup moins fréquemment. Ce n'est toujours pas une garantie complète si votre travailleur peut produire des résultats plus rapidement que le fil de l'interface utilisateur pourrait jamais suivre. Dans ce cas, ralentir le travailleur serait une solution. Facile en utilisant Invoke au lieu de BeginInvoke.

+0

Bon point, Hans! J'ai raté cela :) +1 – Nayan

+0

Merci :) J'ai ajouté Thread.Sleep (10) avant d'appeler backgroundWorker1.ReportProgress et ça marche plutôt bien. Je vais également ajouter des résultats à List <> afin que backgroundWorker1.ReportProgress soit appelé moins souvent – Pawel

0

Si ce programme est exécuté de manière asynchrone, vous pouvez avoir un formulaire qui vous répond.

En outre, les problèmes:

  1. Vous exécutez la boucle dans une autre fonction - il rend l'opération non reponsive.

  2. Vous n'êtes pas même vérifier si l'utilisateur veut annuler (juste un point que je voulais faire) - Poignée DoWorkEventArgs propriété de l » intérieur de la boucle Cancelforeach.

Déplacer le code de la fonction CzyscPliki dans le backgroundWorker1_DoWork (il est de toute façon trop petit).

EDIT:

Si vous ne voulez pas déplacer le code dans gestionnaire d'événements DoWork, alors mieux utiliser Thread pour plus de contrôle. Je ne suis pas un expert, mais vous trouverez beaucoup de code sur la façon de l'implémenter.

+0

Mais CzyscPliki est déjà appelé depuis backgroundWorker1_DoWork, donc il est déjà en cours d'exécution sur un thread différent. – kevev22

+0

Ce n'est pas le but. Le problème est en boucle dans la fonction externe, pas que la fonction externe n'est pas appelée. – Nayan

+0

La fonction CzyscPliki est également utilisée ailleurs, et même si je déplace son code dans backgroundWorker1_DoWork cela ne fonctionne toujours pas. Pouvez-vous s'il vous plaît me donner un exemple de code qui utilisera Background Worker? Disons que while (vrai) loop worker est en train de changer le texte des formulaires à l'heure actuelle. Cette annulation de requiers ofcourse – Pawel