Faire un BackgroundWorker faire plusieurs opérations de manière séquentielle sans geler le formulaire

J’ai déjà posé une question quelque peu similaire ici, mais j’ai maintenant une question de suivi.

Je dois lancer le programme externe plusieurs fois de suite, mais cela me pose plusieurs problèmes:

  • Il a essayé de lancer simultanément toutes les opérations. J’ai mis un “while” ((bgwkSVN.IsBusy) {} “vide, ça marche un peu, mais je suis sûr que cela fera un peu pleurer certains d’entre vous.
  • Il bloque toujours le formulaire tant que toutes les opérations ne sont pas terminées. Compte tenu de quelques autres sujets SO, je pense que, dans l’écriture de mon code, l’application n’est pas vraiment multithread ou que je n’en profite pas … mais je ne connais pas vraiment le threading.
  • Il ne semble pas faire ce que je lui demande de faire. Je vais essayer avec une opération plus simple, pour voir si l’opération échoue ou si le backgroundworker n’est jamais lancé.

Voici le code (désolé, c’est un peu long):

private struct svnCommand { public svnCommand(ssortingng args, ssortingng path, int pourcent) { this.args = args; this.path = path; this.pourcent = pourcent; } public ssortingng args; public ssortingng path; public int pourcent; } private BackgroundWorker bgwkSVN; public Merger() { InitializeComponent(); InitializeBackgroundWorker(); this.textBoxCheminRacine.Text = cheminRacine; } private void MergerRevisions(object sender, EventArgs e) { activerControles(false); textBoxOutput.Text = ""; cheminRacine = textBoxCheminRacine.Text; if (!cheminRacine.EndsWith("\\")) { cheminRacine = cheminRacine + "\\"; } ssortingng branchToMerge = this.textBoxBranche.Text; if (branchToMerge.StartsWith("/")) { branchToMerge = branchToMerge.Subssortingng(1); } // révision(s) ssortingng revisions = ""; foreach (ssortingng r in textBoxRevision.Text.Split(',')) { int rev; if (int.TryParse(r, out rev)) { revisions += ssortingng.Format(" -r {0}:{1}", rev - 1, rev); } else { revisions += " -r " + r.Replace("-", ":"); } } // pourcentage de complétion pour chaque étape int stepPourcent = (int)Math.Floor((double)(100 / (3 + Directory.GetDirectories(cheminRacine + "twigs").Length))); // merge sur le trunk while (bgwkSVN.IsBusy) { } bgwkSVN.RunWorkerAsync(new svnCommand(ssortingng.Format("merge --accept postpone {0} {1}{2} .", revisions, svnbasepath, branchToMerge), cheminRacine + "trunk", stepPourcent)); // merge sur chaque twig ssortingng[] twigs = Directory.GetDirectories(cheminRacine + "twigs"); foreach (ssortingng b in twigs) { while (bgwkSVN.IsBusy) { } bgwkSVN.RunWorkerAsync(new svnCommand(ssortingng.Format("merge --accept postpone {0} {1}{2} .", revisions, svnbasepath, branchToMerge), b, stepPourcent)); } // virer les mergeinfo while (bgwkSVN.IsBusy) { } bgwkSVN.RunWorkerAsync(new svnCommand("pd svn:mergeinfo . -R", cheminRacine, stepPourcent)); // svn update while (bgwkSVN.IsBusy) { } bgwkSVN.RunWorkerAsync(new svnCommand("update", cheminRacine, stepPourcent)); textBoxOutput.Text += Environment.NewLine + "Terminé."; MessageBox.Show("Merge terminé.", "Merge terminé", MessageBoxButtons.OK); // réactiver les champs et boutons activerControles(true); } ///  /// Set up the BackgroundWorker object by attaching event handlers ///  private void InitializeBackgroundWorker() { bgwkSVN = new BackgroundWorker(); bgwkSVN.WorkerReportsProgress = true; bgwkSVN.WorkerSupportsCancellation = true; bgwkSVN.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork); bgwkSVN.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); bgwkSVN.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged); } ///  /// Exécuter une commande SVN ///  private ssortingng SVNcmd(svnCommand s, BackgroundWorker worker, DoWorkEventArgs e) { ssortingng o = ""; o += s.path + Environment.NewLine + s.args + Environment.NewLine; if (worker.CancellationPending) { e.Cancel = true; } else { Process p = new Process(); p.StartInfo.WorkingDirectory = s.path; p.StartInfo.FileName = "svn"; p.StartInfo.Arguments = s.args; p.StartInfo.CreateNoWindow = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.UseShellExecute = false; p.Start(); o += p.StandardOutput.ReadToEnd() + Environment.NewLine; p.WaitForExit(); if (s.pourcent > 0) { worker.ReportProgress(s.pourcent); } } return o; } ///  /// Where the actual, potentially time-consuming work is done. ///  private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // Get the BackgroundWorker that raised this event. BackgroundWorker worker = sender as BackgroundWorker; // Assign the result of the computation to the Result property of the DoWorkEventArgs // object. This is will be available to the RunWorkerCompleted eventhandler. e.Result = SVNcmd((svnCommand)e.Argument, worker, e); } ///  /// Deals with the results of the background operation ///  private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // First, handle the case where an exception was thrown. if (e.Error != null) { MessageBox.Show(e.Error.Message); } else if (e.Cancelled) { textBoxOutput.Text += Environment.NewLine + "Annulé."; } else { textBoxOutput.Text += e.Result.ToSsortingng(); } } ///  /// Updates the progress bar ///  private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { this.progressBarTraitement.Value += e.ProgressPercentage; } 

Merci !

La solution est simple: demandez à un BGW d’exécuter toutes les commandes et non à un seul BGW pour chaque commande. Vous aurez besoin d’une List pour stocker les commandes afin de pouvoir les transmettre facilement à RunWorkerAsync (). DoWork () peut simplement parcourir la liste avec foreach.

the while (bgwkSVN.IsBusy) { } attend dans une boucle serrée et semble causer des retards. Je divisais le processus en plusieurs threads d’arrière-plan et commençais le «suivant» dans backgroundWorkerX_RunWorkerCompleted.

Donc, nobugz vous donne déjà la bonne direction, mais pour être complet, voici un exemple de code:

 using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Windows.Forms; namespace Threading { public partial class FormMain : Form { private BackgroundWorker _BackgroundWorker; private Queue> _Commands; private Random _Random; public FormMain() { InitializeComponent(); _Random = new Random(); _Commands = new Queue>(); _BackgroundWorker = new BackgroundWorker(); _BackgroundWorker.WorkerReportsProgress = true; _BackgroundWorker.WorkerSupportsCancellation = true; _BackgroundWorker.DoWork += backgroundWorker_DoWork; _BackgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged; _BackgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted; _BackgroundWorker.RunWorkerAsync(); } private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) { while (!_BackgroundWorker.CancellationPending) { if (_Commands.Count > 0) { AddMessage("Starting waiting job..."); AddMessage(_Commands.Dequeue().Invoke()); } Thread.Sleep(1); } } void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { progressBar.Value = e.ProgressPercentage; } private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { AddMessage("BackgroundWorker doesn't make any further jobs."); } private void buttonStart_Click(object sender, EventArgs e) { _Commands.Enqueue(DoSomething); //or maybe with a lambda //_Commands.Enqueue(new Func(() => //{ // ssortingng message; // message = DoSomething(); // return message; //})); } private ssortingng DoSomething() { int max = 10; for (int i = 1; i <= max; i++) { Thread.Sleep(_Random.Next(10, 1000)); if (_BackgroundWorker.CancellationPending) { return "Job aborted!"; } AddMessage(String.Format("Currently working on item {0} of {1}", i, max)); _BackgroundWorker.ReportProgress((i*100)/max); } return "Job is done."; } private void AddMessage(string message) { if (textBoxOutput.InvokeRequired) { textBoxOutput.BeginInvoke(new Action(AddMessageInternal), message); } else { AddMessageInternal(message); } } private void AddMessageInternal(ssortingng message) { textBoxOutput.AppendText(Ssortingng.Format("{0:G} {1}{2}", DateTime.Now, message, Environment.NewLine)); textBoxOutput.SelectionStart = textBoxOutput.Text.Length; textBoxOutput.ScrollToCaret(); } private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { if (_BackgroundWorker.IsBusy) { _BackgroundWorker.CancelAsync(); e.Cancel = true; AddMessage("Please close only if all jobs are done..."); } } } } 

Le fait que vous ayez un while (bgwkSVN.IsBusy) { } dans le fil de votre formulaire principal explique pourquoi votre formulaire cesse de répondre. L’agent d’arrière-plan exécute son travail sur un thread distinct, mais votre thread d’interface utilisateur est bloqué. Vous devez envisager de démarrer une méthode RunWorkerAsync () dans l’appel MergerRevisions , puis de lancer la suivante dans l’événement bgwkSVN.RunWorkerCompleted .

Si vous cherchez une solution rapide, c’est la mauvaise façon de le faire, c’est:

Changement:

 while (bgwkSVN.IsBusy) { } 

À:

 while (bgwkSVN.IsBusy) { System.Threading.Thread.Sleep(1000); // Make the current (UI/Form) thread sleep for 1 second Application.DoEvents(); }