2010-06-04 73 views
5

我有線程,它處理一些分析工作。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

回答

6

你有一個僵局。您的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()

1

的調用語句將等到主線程的消息泵不旺,並能處理新的消息。如果您的主線程繁忙,則調用將掛起。

就你而言,它看起來像你的頂級代碼運行在一個緊密的循環中,所以底層代碼中的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

1

從來沒有,每次使用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

4

這是因爲你在你的工作線程的加盟。你的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(); 
    } 

與其他人一樣已經表示,使用睡眠,尤其是在如此低的區間或者是危險的,誤導性的或毫無價值的。我認爲這是毫無價值的陣營。

1

您也可以具有不同的線程同時訪問電網的問題。 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); 
      } 
     } 
    }