c# Thread issue using Invoke from a background thread

前端 未结 5 710
故里飘歌
故里飘歌 2020-12-11 20:07

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

   private static void ThreadProc(object obj)
    {
        var grid = (DataGridView)obj;
        forea         


        
相关标签:
5条回答
  • 2020-12-11 20:22

    It's because you're joining on your worker thread. Your UI thread starts the background thread, then calls Join on it. This stops the UI thread from performing any other actions.

    During this, the background thread is doing its work and calling Invoke, which waits for the UI thread to respond. Because the UI thread is waiting for a Join, it won't ever process the request to invoke. Hence, deadlock.

    What you should do, is eliminate the Join and the MessageBox. Put the MessageBox into its own function.

    void NotifyDone() {
        if( InvokeRequired ) BeginInvoke( (MethodInvoker) NotifyDone );
        else {
            // Perform any post-processing work
            MessageBox.Show("Done", "Info");  
        }
    }
    

    When the background thread is done, simply call this method (and eliminate static from ThreadProc).

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

    And like everyone else has already said, the use of Sleep, especially at such a low interval is either dangerous, misleading or worthless. I'm in the count it worthless camp.

    0 讨论(0)
  • 2020-12-11 20:41

    The Invoke statement will wait until the main thread's message pump isn't busy, and can handle a new message. If your main thread is busy, the Invoke will hang.

    In your case, it looks like your top code is running in a tight loop, so there's never a chance for the Invoke in the bottom code to actually run. If you change the Thread.Sleep in your upper code block to something with a time in it, hopefully that will give your main thread a chance to handle the .Invoke call.

    Depending on what your main application thread is doing, you may need to actually finish your first loop before any of the .Invoke calls will run - if that's the case, I can post some modified code that might work better.

    0 讨论(0)
  • 2020-12-11 20:41

    Never, ever, every use Thread.Sleep(0). It doesn't do what you think it does and will cause you nothing but pain. For example, in a tight loop the OS may decide that the thread that just slept is the next one to run. As a result you won't actually yield the thread.

    Try your code again using Thread.Sleep(1) every N iterations where N is about 0.25 to 1.0 seconds worth of work.

    If that doesn't work let me know and we can look at how ThreadProc is created.

    References

    Never Sleep(0) in an Infinite Loop

    EDIT

    Argument for never using Thread.Sleep

    Thread.Sleep is a sign of a poorly designed program.

    0 讨论(0)
  • 2020-12-11 20:44

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

    0 讨论(0)
  • 2020-12-11 20:48

    You also could be having issues accessing the grid at the same time from different threads. DataTables are not thread safe, so I'd guess that DataGridView isn't either. Here's some sample code from this article on DataRow and Concurrency where you would use Monitor.Enter and Montori.Exit to get some concurrency in place.

        public void DoWorkUpdatingRow(object state)
        {
            List<DataRow> rowsToWorkOn = (List<DataRow>)state;
            foreach (DataRow dr in rowsToWorkOn)
            {
                Monitor.Enter(this);
                try
                {
                    dr["value"] = dr["id"] + " new value";
                }
                finally
                {
                    Monitor.Exit(this);
                }
            }
        }
    
    0 讨论(0)
提交回复
热议问题