2014-09-11 85 views
2

我已經使用VS 2010,C#構建應用程序。Winforms線程應用程序掛起(後臺工作線程)

我在我的應用程序中使用BackgroundWorker

雖然我點擊按鈕代碼從數據庫中獲取記錄並顯示到Datagrid中。 但問題是,當我從代碼運行它的工作正常,但是當我運行程序.exe所以它掛起。

//Declared delegate 
delegate void SetControlPropertyThreadSafeDelegate(Control control, string propertyName, object propertyValue); 

//Declared method to run control Thread safe 
public static void SetControlPropertyThreadSafe(Control control, string propertyName, object propertyValue) 
{ 
    if (control.InvokeRequired) 
    { 
     control.Invoke(new SetControlPropertyThreadSafeDelegate(SetControlPropertyThreadSafe), new object[] { control, propertyName, propertyValue }); 
    } 
    else 
    {    
     control.GetType().InvokeMember(propertyName, BindingFlags.SetProperty, null, control, new object[] { propertyValue }); 
    } 
} 


//calling method like below 
SetControlPropertyThreadSafe(dataGridView1, "DataSource", dtGrid2); 

我不明白我犯的錯誤在哪裏。 爲什麼該程序掛?

+2

一個調試器附加到掛起的應用程序和「破」了。這應該顯示你在這裏「掛起」。 – 2014-09-11 09:22:51

+0

可是當我在調試模式下運行它,它不是掛 – Hardik 2014-09-11 09:25:31

+1

我說:「一個調試器附加**的掛起的應用程序**」,在調試運行它。 VS可以連接到大多數應用程序或線程以進行調試。 – 2014-09-11 09:26:16

回答

3

聽起來好像你正在使用BackgroundWorker ......錯了。

BackgroundWorker類允許你在後臺進行操作,約進步的UI提供的信息,並最終返回一個完整的結果。

由於OP狀態:

委託需要調用線程安全的。 SetControlPropertyThreadSafe(dataGridView1, "DataSource", dtGrid2);在Backgroundworker_DoWork()下運行。當我的應用程序運行時,我需要在主窗口上顯示輸出。

您不應該手動更新DoWork方法中的UI。如果必須以任何方式更新UI,可以使用ProgressChanged事件可在BackgroundWorker類和DoWork執行過程中調用ReportProgress。還請確保將WorkerReportsProgress設置爲true。

ReportProgress方法非常靈活,讓你,如果你必須(在ProgressChangedEventArgs類包含您可以用它來傳遞你想要的任何類型的數據的UserState對象屬性)傳球幾乎所有的東西回到UI。檢查BackgroundWorker MSND page上給出的示例代碼。

這裏是我的例子:

System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker(); 

    void StartBackgroundTask() 
    { 
     worker.DoWork += worker_DoWork; 
     //if it's possible to display progress, use this 
     worker.WorkerReportsProgress = true; 
     worker.ProgressChanged += worker_ProgressChanged; 
     //what to do when the method finishes? 
     worker.RunWorkerCompleted += worker_RunWorkerCompleted; 
     //start! 
     worker.RunWorkerAsync(); 
    } 

    void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e) 
    { 
     //perform any "finalization" operations, like re-enable disabled buttons 
     //display the result using the data in e.Result 
     //this code will be running in the UI thread 
    } 

    //example of a container class to pass more data in the ReportProgress event 
    public class ProgressData 
    { 
     public string OperationDescription { get; set; } 
     public int CurrentResult { get; set; } 
     //feel free to add more stuff here 
    } 

    void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e) 
    { 
     //display the progress using e.ProgressPercentage or e.UserState 
     //this code will be running in the UI thread 
     //UserState can be ANYTHING: 
     //var data = (ProgressData)e.UserState; 
    } 

    void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e) 
    { 
     //this code will NOT be running in the UI thread! 
     //you should NOT call the UI thread from this method 

     int result = 1; 
     //perform calculations 
     for (var i = 1; i <= 10; i++) 
     { 
      worker.ReportProgress(i, new ProgressData(){ OperationDescription = "CustomState passed as second, optional parameter", CurrentResult = result }); 
      System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5)); 
      result *= i; 
     } 

     e.Result = result; 
    } 

除非你必須顯示已經加載的元素給用戶,同時還加載它們,那麼你應該使用ReportProgress顯示只是 - 進步。使用RunWorkerCompleted事件最終將結果傳遞給UI。

如果您使用自己的UI更新代理,那麼您可能會完全消除BackgroundWorker,並使用Task代替。

+0

+1固體答案 – Jeff 2014-09-11 14:27:29

+0

@Shaamaan ..我怎麼可以更新我的這種方法的datagridview?我已經返回下面的代碼,但它並沒有幫助我 – Hardik 2014-09-19 11:12:13

+0

'int output = 1;' 'backgroundWorker1。ReportProgress(output,dtGrid2);' '// SetControlPropertyThreadSafe(dataGridView1,「DataSource」,dtGrid2);''private void backgroundWorker1_ProgressChanged(object sender,ProgressChangedEventArgs e) {progressBar1.Value = e.ProgressPercentage; dataGridView1.DataSource = e; }' – Hardik 2014-09-19 11:12:32

5
void SetControlPropertyThreadSafe(...) 

這樣的方法存在一個大問題。不幸的是很難根除,有方式太多的SO上推薦這個帖子。問題在於它讓程序員睡覺,它是「安全的」,所以它肯定不是問題的原因。將問題轉化爲一個不可違反的問題。

絕對沒有 「安全」 這件事:

  • 使用Control.Invoke()是危險,這很容易造成僵局。當UI線程中有代碼執行一些不明智的事情時觸發,例如等待工作線程完成。只有在背靠牆時才使用Invoke(),實際上需要返回值。當你發現這是必要的,那麼不要這樣做,它總是比賽,除非你禁用UI。始終使用BeginInvoke()代替。

  • 它隱藏了一個消防軟管的問題。你會被調用的方法來更新每一個單一的控制,而不是考慮這個問題。這是調用請求調用UI線程。以比人眼可以看到的高約50倍的速度來做到這一點,你會埋葬UI線程。它一旦發出一個調用請求就無法趕上調用請求,然後又有另一個等待執行。 UI線程現在停止處理其正常職責,例如繪製窗口和處理用戶輸入。它看起來冷凍,就像它將如果你運行此代碼直接

  • 非常不愉快的事情,當用戶關閉窗口,但你的工作線程保持駕駛發生。調用到不再存在的窗口。這通常發出一聲巨響,所以不難診斷。有時它不是不可能調試的,在停止線程和允許窗口關閉之間存在不可避免的線程競爭,只能通過不關閉窗口而是隱藏窗口來解決。

您的問題是第二個項目符號。它會因爲你的代碼現在運行得更快而掛起。 Invoke()調用隱藏了調試器中的問題。完全擺脫這個代碼,它是危險,並從dbase查詢收集結果,例如,列表<>。偶爾將它傳遞給ReportProgress()方法,這樣你就不會使用UI線程。在調用之後重新創建列表,以便它是線程安全的。