簡體   English   中英

Winforms線程應用程序掛起(后台工作線程)

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM