1

The first time I run my backgroundworker it runs correctly - updates a datatable in the background and then RunWorkerCompleted sets the datatable as a datagridview datasource.

If I then run it again, the datagridview clears and doesn't update. I can't work out why.

I've verified that the datatable contains rows when my code hits dgvReadWrites.DataSource.

    private void btnGenerateStats_Click(object sender, EventArgs e)
    {
        dtJobReadWrite.Columns.Clear();
        dtJobReadWrite.Rows.Clear();
        dgvReadWrites.DataSource = dtJobReadWrite;

        List<Tuple<string, string>>jobs = new List<Tuple<string, string>>();

        foreach (ListViewItem job in lstJobs.SelectedItems)
        {
            jobs.Add(new Tuple<string, string>(job.Text, job.SubItems[2].Text));
        }
        BackgroundWorker bgw = new BackgroundWorker();
        bgw.WorkerReportsProgress = true;
        bgw.WorkerSupportsCancellation = true;
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
        pbarGenStats.Style = ProgressBarStyle.Marquee;
        pbarGenStats.MarqueeAnimationSpeed = 30;
        pbarGenStats.Visible = true;
        bgw.RunWorkerAsync(jobs);
    }


    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
        BackgroundWorker bgw = sender as BackgroundWorker;
        List<Tuple<string, string>> jobs = (List<Tuple<string, string>>)e.Argument;
        GetReadWriteStats(jobs);
    }

    private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        BackgroundWorker bgw = sender as BackgroundWorker;
        bgw.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        bgw.DoWork -= new DoWorkEventHandler(bgw_DoWork);
        pbarGenStats.MarqueeAnimationSpeed = 0;
        pbarGenStats.Value = 0;
        pbarGenStats.Visible = false;
        dgvReadWrites.DataSource = dtJobReadWrite;
        dgvReadWrites.Visible = true;
        dgvReadWrites.Refresh();
    } 
David M
  • 2,763
  • 4
  • 21
  • 24
  • 1
    Why are you explicitly unsubscribing to the events on RunWorkerCompleted? – Øyvind Bråthen Oct 03 '11 at 12:42
  • Going off the advice of this thread: http://stackoverflow.com/questions/2542326/proper-way-to-dispose-of-a-backgroundworker which seemed to have a similar problem. Removing those lines doesn't resolve the problem, however. – David M Oct 03 '11 at 12:47
  • You can try to make local backgroundworker variable and just subscribe events once. Then you dont need to remove handlers in complete method – Renatas M. Oct 03 '11 at 12:54
  • 1
    "datagridview clears and doesn't update". Does this mean that bgw_RunWorkerCompleted is not called? Did you test this in debugger? Unsubscribe from events is OK, the problem is somewhere else. – Alex F Oct 03 '11 at 12:57
  • @Alex - yes, you're right: the second time around, it ends "DoWork" and doesn't enter RunWorkerCompleted. – David M Oct 03 '11 at 13:03
  • You have to unbind the grid before you start the worker to fill dtJobReadWrite. Rebind it in the RunWorkerCompleted event handler, like you do. Leaving it bound makes deadlock likely. – Hans Passant Oct 03 '11 at 13:10
  • @Hans - perfect! Setting the dgvReadWrite.DataSource to null in the btn Click handler sorted that out nicely. – David M Oct 03 '11 at 13:53

3 Answers3

3
private void btnGenerateStats_Click(object sender, EventArgs e)
{
    //...
    dgvReadWrites.DataSource = dtJobReadWrite;
    // etc...
}

That's a problem, you are updating dtJobReadWrite in the BGW. That causes the bound grid to get updated by the worker thread. Illegal, controls are not thread-safe and may only be updated from the thread that created them. This is normally checked, producing an InvalidOperationException while debugging but this check doesn't work for bound controls.

What goes wrong next is all over the place, you are lucky that you got a highly repeatable deadlock. The more common misbehavior is occasional painting artifacts and a deadlock only when you are not close. Fix:

    dgvReadWrites.DataSource = null;

and rebinding the grid in the RunWorkerCompleted event handler, like you already do.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

Because you unscubscribe from those events

bgw.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
bgw.DoWork -= new DoWorkEventHandler(bgw_DoWork);

Remove those lines

Albin Sunnanbo
  • 46,430
  • 8
  • 69
  • 108
0

Why are you creating a new BackgroundWorker every time you want to run it? I would like to see what happens with this code if you use one instance of BackgroundWorker (GetReadWriteWorker or something along those lines), subscribe to the events only once, and then run that worker Async on btnGenerateStats_Click.

Michael Fox
  • 611
  • 3
  • 9
  • re-using a component only retains more state. And the OP had a problem with repeatability already. – H H Oct 04 '11 at 20:56