[英]Winforms Thread Application Hang (background worker Thread)
我使用VS 2010,C#構建應用程序。
我在我的應用程序中使用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);
我不明白我做錯了。 為什么該計划掛起?
void SetControlPropertyThreadSafe(...)
像這樣的方法存在很大的問題。 遺憾的是很難根除,有上,這樣推薦這太多的職位。 問題是它讓一個程序員沉睡,它是“安全的”所以肯定不是問題的原因。 將問題轉化為不可解決的問題。
關於它絕對沒有“安全”:
使用Control.Invoke()是危險的 ,它很容易導致死鎖。 當UI線程中的代碼執行某些不明智的操作(如等待工作線程完成)時觸發。 只有當你背對牆時才使用Invoke(),實際上需要返回值。 當你發現那是必要的,那么不要這樣做,它總是比賽,除非你禁用UI。 始終使用BeginInvoke()。
它隱藏了一個消防水帶問題。 你會痴迷於調用方法來更新每一個控件,而不是考慮這個導致的問題。 這是用調用請求打擊UI線程。 這樣做的速度比人眼可以看到的高50倍,你會埋葬UI線程。 它永遠無法趕上調用請求,只要它調度一個然后另一個等待執行。 UI線程現在停止處理其正常職責,例如繪制窗口和處理用戶輸入。 它看起來很冷 ,就像你直接運行這段代碼一樣
當用戶關閉窗口但您的工作線程保持駕駛時,會發生非常不愉快的事情。 調用不再存在的窗口。 這通常會發出響亮的聲音,因此診斷起來並不太難。 有時它不會,不可能調試,在停止線程和允許窗口關閉之間存在不可避免的線程競爭,只能通過不關閉窗口而是隱藏它來解決。
你的問題是第二個子彈。 它掛起是因為你的代碼現在運行得更快。 Invoke()調用隱藏了調試器中的問題。 完全擺脫這個代碼,它是危險的 ,並從例如List <>的dbase查詢中收集結果。 偶爾將它傳遞給ReportProgress()方法,這樣就不會阻塞UI線程。 在調用后重新創建List,因此它是線程安全的。
聽起來你正在使用BackgroundWorker
......錯了。
BackgroundWorker
類允許您在后台執行操作,向UI提供有關進度的信息,最后返回完整的結果。
正如OP所述:
delegate需要調用Thread安全。
SetControlPropertyThreadSafe(dataGridView1, "DataSource", dtGrid2);
在Backgroundworker_DoWork()下運行..當我的應用程序運行時,我需要在主窗口上顯示輸出。
您不應該手動更新DoWork
方法中的UI。 如果必須以任何方式更新UI,則可以使用BackgroundWorker
類上提供的ProgressChanged
事件,並在執行DoWork
期間調用ReportProgress
。 還要確保將WorkerReportsProgress
設置為true。
ReportProgress
方法非常靈活,如果必須,您可以將幾乎所有內容傳遞回UI( ProgressChangedEventArgs
類包含一個UserState
對象屬性,您可以使用它來傳遞您想要的任何類型的數據)。 檢查BackgroundWorker
MSND頁面上給出的示例代碼。
這是我的例子:
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
代替。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.