How to wait correctly until BackgroundWorker completes?

前端 未结 10 1151
既然无缘
既然无缘 2020-12-02 11:19

Observe the following piece of code:

var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;         


        
相关标签:
10条回答
  • 2020-12-02 11:42

    BackgroundWorker has a completion event. Instead of waiting, call your remaining code path from the completion handler.

    0 讨论(0)
  • 2020-12-02 11:45

    To wait for a background worker thread (single or multiple) do the following:

    1. Create a List of Background workers you have programatically created:

      private IList<BackgroundWorker> m_WorkersWithData = new List<BackgroundWorker>();
      
    2. Add the background worker in the list:

      BackgroundWorker worker = new BackgroundWorker();
      worker.DoWork += new DoWorkEventHandler(worker_DoWork);
      worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged);
      worker.WorkerReportsProgress = true;
      m_WorkersWithData.Add(worker);
      worker.RunWorkerAsync();
      
    3. Use the following function to wait for all workers in the List:

      private void CheckAllThreadsHaveFinishedWorking()
      {
          bool hasAllThreadsFinished = false;
          while (!hasAllThreadsFinished)
          {
              hasAllThreadsFinished = (from worker in m_WorkersWithData
                                       where worker.IsBusy
                                       select worker).ToList().Count == 0;
              Application.DoEvents(); //This call is very important if you want to have a progress bar and want to update it
                                      //from the Progress event of the background worker.
              Thread.Sleep(1000);     //This call waits if the loop continues making sure that the CPU time gets freed before
                                      //re-checking.
          }
          m_WorkersWithData.Clear();  //After the loop exits clear the list of all background workers to release memory.
                                      //On the contrary you can also dispose your background workers.
      }
      
    0 讨论(0)
  • 2020-12-02 11:45

    This question is old but I don't think the author got the answer he was looking for.

    This is a bit dirty, and it's in vb.NET but works for me

    Private Sub MultiTaskingForThePoor()
        Try
            'Start background worker
            bgwAsyncTasks.RunWorkerAsync()
            'Do some other stuff here
            For i as integer = 0 to 100
                lblOutput.Text = cstr(i)
            Next
    
            'Wait for Background worker
            While bgwAsyncTasks.isBusy()
                Windows.Forms.Application.DoEvents()
            End While
    
            'Voila, we are back in sync
            lblOutput.Text = "Success!"
        Catch ex As Exception
            MsgBox("Oops!" & vbcrlf & ex.Message)
        End Try
    End Sub
    
    0 讨论(0)
  • 2020-12-02 11:51

    Checking backgrWorker.IsBusy in the loop with Application.DoEvents() is not a nicely way.

    I agree with @JohannesH, you should definitively use AutoResetEvent as a elegant solution. But not using it in UI Thread, it will cause main thread blocked; it should come from another background worker thread.

    AutoResetEvent aevent = new AutoResetEvent(false);    
    private void button1_Click(object sender, EventArgs e)
    {
        bws = new BackgroundWorker();
        bws.DoWork += new DoWorkEventHandler(bw_work);
        bws.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_complete);
        bws.RunWorkerAsync();
    
        bwWaiting.DoWork += new DoWorkEventHandler(waiting_work);
        bwWaiting.RunWorkerCompleted += new RunWorkerCompletedEventHandler(waiting_complete);
        bwWaiting.RunWorkerAsync();
    }
    
    void bw_work(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(2000);
    }
    
    void bw_complete(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("complete " + bwThread.ToString());
        aevent.Set();
    }
    void waiting_work(object sender, DoWorkEventArgs e)
    {
        aevent.WaitOne();
    }
    
    void waiting_complete(object sender, RunWorkerCompletedEventArgs e)
    {
        Debug.WriteLine("complete waiting thread");
    }
    
    0 讨论(0)
  • 2020-12-02 11:52

    VB.NET

    While BackgroundWorker1.IsBusy()
        Windows.Forms.Application.DoEvents()
    End While
    

    You can use this to chain multiple events. (sudo code to follow)

    download_file("filepath")
    
        While BackgroundWorker1.IsBusy()
           Windows.Forms.Application.DoEvents()
        End While
    'Waits to install until the download is complete and lets other UI events function install_file("filepath")
    While BackgroundWorker1.IsBusy()
        Windows.Forms.Application.DoEvents()
    End While
    'Waits for the install to complete before presenting the message box
    msgbox("File Installed")
    
    0 讨论(0)
  • 2020-12-02 11:53

    I was also looking for a suitable solution. I solved the waiting with an exclusive lock. The critical path in code are writing to a public container (here the console) and increasing or decreasing the workers. No thread should interfere while writing to this variable, otherwise the count is not guaranteed anymore.

    public class Program
    {
        public static int worker = 0;
        public static object lockObject = 0;
    
        static void Main(string[] args)
        {
    
            BackgroundworkerTest backgroundworkerTest = new BackgroundworkerTest();
            backgroundworkerTest.WalkDir("C:\\");
            while (backgroundworkerTest.Worker > 0)
            {
                // Exclusive write on console
                lock (backgroundworkerTest.ExclusiveLock)
                {
                    Console.CursorTop = 4; Console.CursorLeft = 1;
                    var consoleOut = string.Format("Worker busy count={0}", backgroundworkerTest.Worker);
                    Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth-consoleOut.Length));
                }
            }
        }
    }
    
    public class BackgroundworkerTest
    {
        private int worker = 0;
        public object ExclusiveLock = 0;
    
        public int Worker
        {
            get { return this.worker; }
        }
    
        public void WalkDir(string dir)
        {
            // Exclusive write on console
            lock (this.ExclusiveLock)
            {
                Console.CursorTop = 1; Console.CursorLeft = 1;
                var consoleOut = string.Format("Directory={0}", dir);
                Console.Write("{0}{1}", consoleOut, new string(' ', Console.WindowWidth*3 - consoleOut.Length));
            }
    
            var currentDir = new System.IO.DirectoryInfo(dir);
            DirectoryInfo[] directoryList = null;
            try
            {
                directoryList = currentDir.GetDirectories();
            }
            catch (UnauthorizedAccessException unauthorizedAccessException)
            {
                // No access to this directory, so let's leave
                return;
            }
    
            foreach (var directoryInfo in directoryList)
            {
                var bw = new BackgroundWorker();
    
                bw.RunWorkerCompleted += (sender, args) =>
                {
                    // Make sure that this worker variable is not messed up
                    lock (this.ExclusiveLock)
                    {
                        worker--;
                    }
                };
    
                DirectoryInfo info = directoryInfo;
                bw.DoWork += (sender, args) => this.WalkDir(info.FullName);
    
                lock (this.ExclusiveLock)
                {
                    // Make sure that this worker variable is not messed up
                    worker++;
                }
                bw.RunWorkerAsync();
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题