How to wait correctly until BackgroundWorker completes?

六月ゝ 毕业季﹏ 提交于 2019-11-27 11:13:22
JohannesH

Try using the AutoResetEvent class like this:

var doneEvent = new AutoResetEvent(false);
var bw = new BackgroundWorker();

bw.DoWork += (sender, e) =>
{
  try
  {
    if (!e.Cancel)
    {
      // Do work
    }
  }
  finally
  {
    doneEvent.Set();
  }
};

bw.RunWorkerAsync();
doneEvent.WaitOne();

Caution: You should make sure that doneEvent.Set() is called no matter what happens. Also you might want to provide the doneEvent.WaitOne() with an argument specifying a timeout period.

Note: This code is pretty much a copy of Fredrik Kalseth answer to a similar question.

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.
    }
    

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

goughy000

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
Don

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")

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");
}

not quite sure what u mean by waiting. Do you mean that you want something done (by the BW) after thats done you want to do something else? Use bw.RunWorkerCompleted like you do (use a seperate function for readability) and in that callback function do you next stuff. Start a timer to check if the work doesnt take too long.

var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
  OnAsyncOperationCompleted(sender, args);
});
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);

Timer Clock=new Timer();
Clock.Interval=1000;
Clock.Start();
Clock.Tick+=new EventHandler(Timer_Tick);

public void Timer_Tick(object sender,EventArgs eArgs)
{   
    if (bw.WorkerSupportsCancellation == true)
    {
        bw.CancelAsync();
    }

    throw new TimedoutException("bla bla bla");
 }

In the OnDoWorkLoadChildren:

if ((worker.CancellationPending == true))
{
    e.Cancel = true;
    //return or something
}

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();
        }
    }
}

I used Tasks with a BackgroundWorker

You can create any number of tasks and add them to a list of tasks. The worker will start when a task is added, restart if a task is added while the worker IsBusy, and stop once there are no more tasks.

This will allow you to update the GUI asynchronously as much as you need to without freezing it.

This works as is for me.

    // 'tasks' is simply List<Task> that includes events for adding objects
    private ObservableCollection<Task> tasks = new ObservableCollection<Task>();
    // this will asynchronously iterate through the list of tasks 
    private BackgroundWorker task_worker = new BackgroundWorker();

    public Form1()
    {
        InitializeComponent();
        // set up the event handlers
        tasks.CollectionChanged += tasks_CollectionChanged;
        task_worker.DoWork += task_worker_DoWork;
        task_worker.RunWorkerCompleted += task_worker_RunWorkerCompleted;
        task_worker.WorkerSupportsCancellation = true;

    }

    // ----------- worker events
    void task_worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (tasks.Count != 0)
        {
            task_worker.RunWorkerAsync();
        }
    }

    void task_worker_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {

            foreach (Task t in tasks)
            {
                t.RunSynchronously();
                tasks.Remove(t);
            }
        }
        catch
        {
            task_worker.CancelAsync();
        }
    }


    // ------------- task event
    // runs when a task is added to the list
    void tasks_CollectionChanged(object sender,
        System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        if (!task_worker.IsBusy)
        {
            task_worker.RunWorkerAsync();
        }
    }

Now all you need is to create a new Task and add it to the List<>. It will be run by the worker in the order it was placed into the List<>

Task t = new Task(() => {

        // do something here
    });

    tasks.Add(t);

In OpenCV exists function WaitKey. Ir allows solve this issue in that way:

while (this->backgroundWorker1->IsBusy) {
    waitKey(10);
    std::cout << "Wait for background process: " << std::endl;
}
this->backgroundWorker1->RunWorkerAsync();
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!