2009-02-06 5 views
0

J'ai implémenté le filetage dans mon application pour graver des sites Web. Une fois tous les sites mis au rebut, je veux les traiter. Le formulaire crée un travailleur de la file d'attente (qui crée 2 travailleurs et traite les tâches). Une fois toutes les tâches terminées, je veux les traiter dans le formthread. A ce stade, j'accieved cela avecProgression du fil vers l'interface graphique

public void WaitForCompletion() 
    { 
     // Enqueue one null task per worker to make each exit. 
     StopWorkers(); 

     //wait for the workers to finish 
     foreach (Thread worker in workers) 
     { 
      worker.Join(); 
     } 
    } 

Après une tâche préformées je tire (de queueworker):

public event EventHandler<ProgressEvent> UrlScanned; 

if (UrlScanned != null) 
{ 
     UrlScanned(this, new ProgressEvent(task.Name, 1)); 
} 

et attraper cet événement avec:

urlscanner.UrlScanned += new EventHandler<ProgressEvent>(UrlScanningProgress);  

private void UrlScanningProgress(object sender, ProgressEvent args) 
{ 
    if (pbarurlScan.InvokeRequired) 
    { 
     //don't use invoke when Thread.Join() is used! Deadlock 
     Invoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args); 
     //BeginInvoke(new MethodInvoker(delegate() { UrlScanningProgress(sender, args)}; 
    } 
    else 
    { 
       pbarurlScan.Value++; 
    } 
} 

Le problème est que le thread de formulaire est bloqué et en appelant Invoke l'application entière est maintenant dans une situation de blocage.

Comment puis-je donner à la mise à jour formthread sans avoir une impasse et ont une mise à jour immidiate (BeginInvoke se produit si les fils des travailleurs se font)

Répondre

2

Pourquoi fais-tu l'inscription? Je lève un rappel à la fin de chaque thread - peut-être décrémentation d'un compteur. Lorsque le compteur arrive à 0, puis rappeler à l'UI thread et faire le code "tout fait"; quelque chose comme:

using System.Windows.Forms; 
using System.Threading; 
using System; 
class MyForm : Form 
{ 

    public MyForm() 
    { 
     Button btn = new Button(); 
     Controls.Add(btn); 
     btn.Text = "Go"; 
     btn.Click += btn_Click; 
    } 
    int counter; 
    void btn_Click(object sender, System.EventArgs e) 
    { 
     for (int i = 0; i < 5; i++) 
     { 
      Interlocked.Increment(ref counter); 
      ThreadPool.QueueUserWorkItem(DoWork, i); 
     } 
    } 
    void DoWork(object state) 
    { 
     for (int i = 0; i < 10; i++) 
     { // send progress 
      BeginInvoke((Action)delegate { Text += state.ToString(); }); 
      Thread.Sleep(500); 
     } 
     EndThread(); // this thread done 
    } 
    void EndThread() 
    { 
     if (Interlocked.Decrement(ref counter) == 0) 
     { 
      AllDone(); 
     } 
    } 
    void AllDone() 
    { 
     Invoke((Action)delegate { this.Text += " all done!"; }); 
    } 
    [STAThread] 
    static void Main() 
    { 
     Application.Run(new MyForm()); 
    } 
} 
+0

J'ai utilisé la jointure pour attendre 'tout simplement' que tout soit fait. Créez donc un gestionnaire UrlScanningComplete dans le formulaire. Et déclencher un événement à partir du travailleur de la file d'attente si toutes les tâches sont effectuées? – RvdK

+0

Cette approche serait correcte - mais vous devez toujours basculer vers le thread d'interface utilisateur (Invoke/BeginInvoke) à un moment donné avant d'effectuer un travail réel sur le formulaire. –

0

Je déclencher un événement séparé lorsque toutes les tâches sont effectuées. J'ai déplacé la logique pour traiter les tâches à une fonction séparée qui sera appelée lors de la réception de l'événement AllUrlsScanned.

Questions connexes