winforms 如何正确停止BackgroundWorker

qgelzfjb  于 2022-12-23  发布在  其他
关注(0)|答案(8)|浏览(176)

我有一个表单,上面有两个组合框。我想根据combobox1.Textcombobox2.Text填充combobox2.DataSource(我假设用户已经完成了combobox1中的输入,并且正在combobox2中输入)。所以我有一个combobox2的事件处理程序,如下所示:

private void combobox2_TextChanged(object sender, EventArgs e)
{
    if (cmbDataSourceExtractor.IsBusy)
       cmbDataSourceExtractor.CancelAsync();

    var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
       V2 = combobox2.Text};
    cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}

由于构建DataSource是一个耗时的过程(它创建一个对数据库的请求并执行它),我决定最好使用BackgroundWorker在另一个进程中执行它。因此,存在这样一个场景:cmbDataSourceExtractor尚未完成其工作,用户又键入了一个符号。在这种情况下,我在此行中得到了一个异常
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );有关BackgroundWorker正忙碌,无法同时执行多个操作的信息。
如何摆脱这个异常?

vsikbqxv

vsikbqxv1#

CancelAsync实际上不会中止线程或类似的操作。它会向工作线程发送一条消息,指示应该通过BackgroundWorker.CancellationPending取消工作。正在后台运行的DoWork委托必须定期检查此属性,并自行处理取消操作。
棘手的部分是DoWork委托可能阻塞,这意味着必须先完成对DataSource所做的工作,然后才能执行其他操作(如检查CancellationPending)。您可能需要将实际工作移到另一个异步委托(或者更好的是,将工作提交给ThreadPool),并让您的主工作线程进行轮询,直到这个内部工作线程触发等待状态,或者它检测到CancellationPending。
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx
http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx

gtlvzcf8

gtlvzcf82#

如果在CancelAsync()和RunWorkerAsync()之间添加一个循环,就可以解决问题

private void combobox2_TextChanged(object sender, EventArgs e)
 {
     if (cmbDataSourceExtractor.IsBusy)
        cmbDataSourceExtractor.CancelAsync();

     while(cmbDataSourceExtractor.IsBusy)
        Application.DoEvents();

     var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
        V2 = combobox2.Text};
     cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
  }

调用Application.DoEvents()的while循环将暂停新工作线程的执行,直到当前工作线程被正确取消。请记住,您仍然需要处理工作线程的取消。

private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
 {
      if (this.cmbDataSourceExtractor.CancellationPending)
      {
          e.Cancel = true;
          return;
      }
      // do stuff...
 }

第一个代码片段中的Application.DoEvents()将继续处理GUI线程消息队列,因此即使取消并更新cmbDataSourceExtractor.IsBusy属性也将继续处理(如果您只是添加了continue而不是Application.DoEvents(),则循环将GUI线程锁定为繁忙状态,并且不会处理更新cmbDataSourceExtractor.IsBusy的事件)

4ktjp1zp

4ktjp1zp3#

您必须使用主线程和BackgroundWorker之间共享的标志,例如BackgroundWorker.CancellationPending。当您希望BackgroundWorker退出时,只需使用BackgroundWorker.CancelAsync()设置该标志。
MSDN提供了一个示例:http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx

5lwkijsr

5lwkijsr4#

我的例子。DoWork如下:

DoLengthyWork();

    //this is never executed
    if(bgWorker.CancellationPending)
    {
        MessageBox.Show("Up to here? ...");
        e.Cancel = true;
    }

DoLengtyWork内部:

public void DoLenghtyWork()
{
    OtherStuff();
    for(int i=0 ; i<10000000; i++) 
    {  int j = i/3; }
}

在其他内容()中:

public void OtherStuff()
{
    for(int i=0 ; i<10000000; i++) 
    {  int j = i/3; }
}

您要做的是修改DoLengtyWork和OtherStuff(),使它们成为:

public void DoLenghtyWork()
{
    if(!bgWorker.CancellationPending)
    {              
        OtherStuff();
        for(int i=0 ; i<10000000; i++) 
        {  
             int j = i/3; 
        }
    }
}

public void OtherStuff()
{
    if(!bgWorker.CancellationPending)
    {  
        for(int i=0 ; i<10000000; i++) 
        {  
            int j = i/3; 
        }
    }
}
sshcrbum

sshcrbum5#

此问题是由于cmbDataSourceExtractor.CancelAsync()是异步方法,cmdDataSourceExtractor.RunWorkerAsync(...)退出时Cancel操作尚未完成。您应该等待cmdDataSourceExtractor完成后再调用RunWorkerAsyncin this SO question解释了如何执行此操作。

cgyqldqp

cgyqldqp6#

我的答案有点不同,因为我尝试过这些方法,但它们不起作用。我的代码使用了一个额外的类,在读取数据库值时,或者在将对象添加到List对象或类似对象之前,在公共静态类中检查布尔标志。请参见下面代码中的更改。我添加了ThreadWatcher.StopThread属性。对于此解释,我将"我不打算恢复当前线程,因为这不是您的问题,但这很简单,只需在访问下一个线程之前将属性设置为false即可...

private void combobox2_TextChanged(object sender, EventArgs e)
 {
  //Stop the thread here with this
     ThreadWatcher.StopThread = true;//the rest of this thread will run normally after the database function has stopped.
     if (cmbDataSourceExtractor.IsBusy)
        cmbDataSourceExtractor.CancelAsync();

     while(cmbDataSourceExtractor.IsBusy)
        Application.DoEvents();

     var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
        V2 = combobox2.Text};
     cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
  }

都很好

private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
 {
      if (this.cmbDataSourceExtractor.CancellationPending)
      {
          e.Cancel = true;
          return;
      }
      // do stuff...
 }

现在添加以下类

public static class ThreadWatcher
{
    public static bool StopThread { get; set; }
}

在你读数据库的课上

List<SomeObject>list = new List<SomeObject>();
...
if (!reader.IsDbNull(0))
    something = reader.getString(0);
someobject = new someobject(something);
if (ThreadWatcher.StopThread == true)
    break;
list.Add(something);
...

别忘了使用finally块来正确地关闭你的数据库连接等等。希望这能有所帮助!如果你觉得有帮助,请标记我。

dzjeubhm

dzjeubhm7#

在我的情况下,我必须池数据库支付确认进来,然后更新WPF用户界面。

加速所有进程的机制:

public void Execute(object parameter)
        {
            try
            {
                var url = string.Format("{0}New?transactionReference={1}", Settings.Default.PaymentUrlWebsite, "transactionRef");
                Process.Start(new ProcessStartInfo(url));
                ViewModel.UpdateUiWhenDoneWithPayment = new BackgroundWorker {WorkerSupportsCancellation = true};
                ViewModel.UpdateUiWhenDoneWithPayment.DoWork += ViewModel.updateUiWhenDoneWithPayment_DoWork;
                ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerCompleted += ViewModel.updateUiWhenDoneWithPayment_RunWorkerCompleted;
                ViewModel.UpdateUiWhenDoneWithPayment.RunWorkerAsync();
            }
            catch (Exception e)
            {
                ViewModel.Log.Error("Failed to navigate to payments", e);
                MessageBox.Show("Failed to navigate to payments");
            }
        }

检查完成情况的机制:

private void updateUiWhenDoneWithPayment_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(30000);
        while (string.IsNullOrEmpty(GetAuthToken()) && !((BackgroundWorker)sender).CancellationPending)
        {
            Thread.Sleep(5000);
        }

        //Plug in pooling mechanism
        this.AuthCode = GetAuthToken();
    }

窗口关闭时取消的机制:

private void PaymentView_OnUnloaded(object sender, RoutedEventArgs e)
    {
        var context = DataContext as PaymentViewModel;
        if (context.UpdateUiWhenDoneWithPayment != null && context.UpdateUiWhenDoneWithPayment.WorkerSupportsCancellation && context.UpdateUiWhenDoneWithPayment.IsBusy)
            context.UpdateUiWhenDoneWithPayment.CancelAsync();
    }
wfauudbj

wfauudbj8#

我同意男人的看法。但有时候你得多加点东西。
工业工程
1)添加此worker.WorkerSupportsCancellation = true;
2)在类中添加一些方法来完成以下操作

public void KillMe()
{
   worker.CancelAsync();
   worker.Dispose();
   worker = null;
   GC.Collect();
}

因此在关闭应用程序之前,您必须调用此方法。
3)也许你可以Dispose, null所有的变量和定时器都在BackgroundWorker里面。

相关问题