2014-09-11 84 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线程。在调用之后重新创建列表,以便它是线程安全的。