c#线程问题使用从后台线程调用

问题描述:

我有线程,它处理一些分析工作。c#线程问题使用从后台线程调用

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

我想安全地循环更新我的GridView,所以我用经典的方式:

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; 

     } 
    } 

但是,当我进入UpdateGridSafe,程序挂起。

在调试器中,我看到grid.Invoke不调用UpdateGridSafe。请帮忙 - 怎么了?

编辑

经典线程创建代码

 Thread t = new Thread(new ParameterizedThreadStart(ThreadProc)); 
     t.Start(dgvSource); 
     t.Join(); 
     MessageBox.Show("Done", "Info"); 
+0

你为什么使用thread.sleep?如果你想在更新网格后执行任何操作,你可以在这里使用回调函数吗? – Ram 2010-06-04 04:19:26

你有一个僵局。您的t.Join会阻止GUI线程,直到完成ThreadProc。 ThreadProc被阻塞等待t.Join完成,因此它可以完成调用。

糟糕的代码

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

优良程序代码

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

编辑

而且使用的BeginInvoke,而不是调用。这样,每当你更新GUI时,你的工作线程就不必阻塞。

参考

Avoid Invoke(), prefer BeginInvoke()

的调用语句将等到主线程的消息泵不旺,并能处理新的消息。如果您的主线程繁忙,则调用将挂起。

就你而言,它看起来像你的*代码运行在一个紧密的循环中,所以底层代码中的Invoke从来没有机会真正运行。如果将上层代码块中的Thread.Sleep更改为有时间的内容,希望这会让主线程有机会处理.Invoke调用。

根据你的主应用程序线程在做什么,你可能需要在任何.Invoke调用运行之前真正完成你的第一个循环 - 如果是这样的话,我可以发布一些可能更好的修改代码。

+0

我改为Thread.Sleep(10);但也得到了相同的情况:(主应用程序线程是ui线程,没有工作,请发布代码,可能会更好。 – 2010-06-04 04:00:01

+0

@Andrew - 如果第一个代码块中的主线程是你的UI线程,而你在一个紧密的循环中运行它,然后它将不会响应任何.Invoke请求,直到它完成了正在运行的forEach中的每一行为止。如果您在另一个线程上调用ThreadProc(),那么您的消息泵应该可用 - .Invoke挂起的事实说明您阻止了您的主UI线程。找出您的主线程停止并解决的位置,并且您的.Invoke调用将再次开始工作。 – SqlRyan 2010-06-04 04:11:20

+0

我创建ThreadProc来自主thrad,用于在后台处理一些工作 – 2010-06-04 04:19:16

从来没有,每次使用Thread.Sleep(0)。它不会做你认为它会做的事情,只会让你痛苦。例如,在紧密的循环中,操作系统可能会决定刚睡的线程是下一个要运行的线程。结果你实际上不会产生线程。

再次尝试使用Thread.Sleep(1)每次N次迭代的代码,其中N大约是0.25到1.0秒的工作量。

如果这不起作用,让我知道,我们可以看看如何创建ThreadProc。

参考

Never Sleep(0) in an Infinite Loop

编辑

性论证从未使用了Thread.Sleep

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

+0

感谢您的回答。我尝试设置睡眠时间,但它没有帮助。另外我添加一些代码发布。 – 2010-06-04 04:11:36

这是因为你在你的工作线程的加盟。你的UI线程启动后台线程,然后调用Join。这会阻止UI线程执行任何其他操作。

在这期间,后台线程正在完成它的工作并调用Invoke,它将等待UI线程作出响应。因为UI线程正在等待Join,所以它不会处理要调用的请求。因此,僵局。

你应该做的是消除Join和MessageBox。将MessageBox放入它自己的函数中。

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

当后台线程完成时,只需调用此方法(并从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(); 
    } 

与其他人一样已经表示,使用睡眠,尤其是在如此低的区间或者是危险的,误导性的或毫无价值的。我认为这是毫无价值的阵营。

您也可以具有不同的线程同时访问电网的问题。 DataTables不是线程安全的,所以我猜DataGridView不是。以下是this article on DataRow and Concurrency中的一些示例代码,您可以使用Monitor.Enter和Montori.Exit来获得一些并发性。

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