【问题标题】:How to stop Backgroundworker and close the form?如何停止 Backgroundworker 并关闭表单?
【发布时间】:2012-03-29 07:51:34
【问题描述】:

我想在运行以关闭表单时完全停止 BackgroundWorker DoWork() 进程。

我应用了以下代码,但在“this.Invoke”中会引发错误:“在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke。”而窗体关闭。

   private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            var dt_Images = db.Rings.Select(I => new { I.PaidRs, I.TypeID, I.RingID, I.CodeNo, Image = Image.FromStream(new MemoryStream(I.Image.ToArray())) }).OrderByDescending(r => r.TypeID);
            foreach (var dr in dt_Images.ThenByDescending(r => r.RingID).ToList())
            {
                BTN = new Button();
                BTN.TextImageRelation = TextImageRelation.TextAboveImage;
                BTN.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
                BTN.AutoSize = true;
                BTN.Name = dr.RingID.ToString();
                BTN.Image = dr.Image;
                BTN.Text = dr.CodeNo.ToString() + "    " + dr.TypeID.ToString();
                this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 != null) flowLayoutPanel1.Controls.Add(BTN); else return; }));
                BTN.Click += new EventHandler(this.pic_Click);
                this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 == null) txt_pcs.Text = flowLayoutPanel1.Controls.Count.ToString(); else return;}));
            }
        }

  private void Form_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Escape)
            {
                backgroundWorker1.CancelAsync(); //backgroundworker doesnt stop here
                backgroundWorker1 = null;   //it still invokes the delegate
                this.Dispose();  
            }
        }

如何解决这个错误?

请帮帮我。

【问题讨论】:

    标签: .net winforms c#-4.0 controls backgroundworker


    【解决方案1】:

    您必须注意工作人员内部的取消请求。喜欢

    private void DoWork(object sender, DoWorkEventArgs e) 
    { 
     var worker = sender as BackgroundWorker; 
    
     while (!worker.CancellationPending) 
     { 
       … 
     } 
    
     if (worker.CancellationPending) 
     { 
      e.Cancel = true; 
     } 
    }
    

    【讨论】:

    • 这还不够。如果你在处理之前不检查工人是否真的停止了,就会有一个竞争条件,每隔一段时间就会导致异常。
    • 我已经应用了上面的代码但是遇到了同样的问题。@Israel Lot
    【解决方案2】:

    实际的问题是,在后台线程中调用Invoke 之前,没有办法安全地检查表单是否正在关闭。

    要解决这个问题,你可以做的是推迟关闭一点,直到后台线程有机会退出主循环。

    首先,声明两个标志和一个锁对象:

    private volatile bool _closeRequest = false;
    private volatile bool _workerStopped = false;
    private readonly object _lock = new object();
    

    然后,当您想关闭表单时,只需调用Close

    private void Form_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyData == Keys.Escape)
        {
            Close();
        }
    }
    

    FormClosing内部,检查worker是否已经停止。这部分必须在监视器内,以防止后台线程刚刚完成时出现竞争条件(即确保_workerStopped_closeRequest 自动更新):

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        lock (_lock)
        {
            // if not stopped
            if (!_workerStopped)
            {
                // delay closing
                e.Cancel = true;
    
                // notify worker
                _closeRequest = true;
            }
        }
        base.OnFormClosing(e);
    }
    

    最后,在您的后台线程中,如果需要,实际关闭表单:

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        try
        {
            foreach (var dr in GetImages())
            {
                if (_closeRequest)
                    break;
    
                // ... do stuff
            }
        }
        finally
        {
            lock (_lock)
            {
                // notify we stopped
                _workerStopped = true;
    
                // if closing was postponed, close now
                if (_closeRequest)
                    BeginInvoke(new MethodInvoker(Close));
            }
        }
    }
    

    【讨论】:

    • 为什么选择 AutoResetEvent 而不是 backgroundWorker1.CancelAsync()?有好处吗?只是好奇。
    • @PoweRoy:你是对的,第一部分也可以通过在worker方法中调用CancelAsync和轮询BackgorundWorker.CancellationPending来完成。这只是稍微通用一点,当一个简单的后台线程用作工作线程时,它会起作用。
    • 使用此方法但在此过程中“_workerStopped.WaitOne();”光标正在等待。 @格鲁
    • @Tulsi:好的,没错,我没有考虑足够,它稍微复杂一些。我稍后会更新我的答案。
    【解决方案3】:

    这是一个简单的程序,我认为它可以让您了解如何在 BackgroundWorker 运行时关闭表单:

    int i = 0;//This is to show progress.
    bool cancel;//This is to notify RunWorkerCompleted to Close() the Form if needed.
    
    public Form1()
    {
        InitializeComponent();
        FormClosing += Form1_FormClosing;
    
        backgroundWorker1.WorkerSupportsCancellation = true;
        backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
        backgroundWorker1.RunWorkerAsync();
    }
    
    void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //Since this is executed on the main thread - it is not (as far as I know) going to "race" against the FormClosing.
        if (cancel) Close();
        else Text = "Done";
    }
    
    void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (backgroundWorker1.IsBusy)
        {
            cancel = true;
            backgroundWorker1.CancelAsync();
            e.Cancel = true;
        }
    }
    
    private void button1_Click(object sender, EventArgs e)
    {
        backgroundWorker1.CancelAsync();
    }
    
    //This is executed on a separate thread:
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        while (!backgroundWorker1.CancellationPending)
        {
            Invoke((Action)(() => Text = (i++).ToString()));
            Thread.Sleep(1000);
        }
        e.Cancel = true;
    }
    

    【讨论】:

      【解决方案4】:

      在您的 DoWork 方法中,您必须在开始处理下一张图像之前检查 backgroundWorker1.CancellationPending

      您必须等到处理完最后一张图片后才能允许用户关闭表单。

      有关如何使用CancellationPendingCancel 属性的完整示例,请参阅this MSDN example

      【讨论】:

      • 我也使用了 MSDN 示例,但出现了同样的问题。@Philip Fourie
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-16
      • 2014-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-18
      • 2023-03-16
      相关资源
      最近更新 更多