Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c# Thread issue using Invoke from a background thread

I've got thread, which processes some analytic work.

   private static void ThreadProc(object obj)
    {
        var grid = (DataGridView)obj;
        foreach (DataGridViewRow row in grid.Rows)
        {
            if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)
                UpdateGridSafe(grid,row.Index,1);
            Thread.Sleep(10);
        }
    }

I want safely update my gridView at loop, so I use classic way:

    private delegate void UpdateGridDelegate(DataGridView grid, int rowIdx, int type);
    public static void UpdateGridSafe(DataGridView grid, int rowIdx, int type)
    {
        if (grid.InvokeRequired)
        {
            grid.Invoke(new UpdateGridDelegate(UpdateGridSafe), new object[] { grid, rowIdx, type });
        }
        else
        {
            if (type == 1)
                grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.Red;
            if (type==2)
                grid.Rows[rowIdx].Cells["Prep"].Style.ForeColor = Color.ForestGreen;

        }
    }

But when I enter UpdateGridSafe, the program hangs.

In the debugger, I see that grid.Invoke doesn't invoke UpdateGridSafe. Please help - what's wrong?

EDIT

Classic thread create code

        Thread t = new Thread(new ParameterizedThreadStart(ThreadProc));
        t.Start(dgvSource);
        t.Join();
        MessageBox.Show("Done", "Info");
like image 310
Andrew Kalashnikov Avatar asked Jun 04 '10 03:06

Andrew Kalashnikov


1 Answers

You have a deadlock. Your t.Join is blocking the GUI thread until ThreadProc is done. ThreadProc is blocked waiting for t.Join to finish so it can do the Invokes.

Bad Code

    Thread t = new Thread(new ParameterizedThreadStart(ThreadProc)); 
    t.Start(dgvSource); 
    t.Join();  <--- DEADLOCK YOUR PROGRAM
    MessageBox.Show("Done", "Info"); 

Good Code

   backgroundWorker1.RunWorkerAsync

  private void backgroundWorker1_DoWork(object sender, 
        DoWorkEventArgs e)
    {    
        var grid = (DataGridView)obj;    
        foreach (DataGridViewRow row in grid.Rows)    
        {    
            if (Parser.GetPreparationByClientNameForSynonims(row.Cells["Prep"].Value.ToString()) != null)    
                UpdateGridSafe(grid,row.Index,1);    
            // don't need this Thread.Sleep(10);    
        }    
    }  

   private void backgroundWorker1_RunWorkerCompleted(
            object sender, RunWorkerCompletedEventArgs e)
        {
        MessageBox.Show("Done", "Info"); 
}

EDIT

Also use BeginInvoke instead of Invoke. That way your worker thread doesn't have to block every time you update the GUI.

Reference

Avoid Invoke(), prefer BeginInvoke()

like image 189
Jonathan Allen Avatar answered Nov 14 '22 22:11

Jonathan Allen