c# - 将参数传递给 backgroundWorker(用作取消按钮)

标签 c# multithreading backgroundworker argument-passing cancel-button

我是 C# 和面向对象编程的新手。我一直在尝试在我的 GUI 中实现一个“取消”按钮,以便用户可以在进程中停止它。

我读了这个问题:How to implement a Stop/Cancel button?并确定 backgroundWorker 对我来说应该是一个不错的选择,但给出的示例没有解释如何将参数传递给 backgroundWorker。

我的问题是我不知道如何将参数传递给 backgroundWorker 以使其停止进程;我只能让 backgroundWorker 自行停止。

我创建了以下代码来尝试学习这一点,其中我的表单有两个按钮(buttonStart 和 buttonStop)和一个 backgroundWorker (backgroundWorkerStopCheck):

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;

namespace TestBackgroundWorker
{
    public partial class Form1 : Form
    {
        public Form1()
        {         
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
        }

        private System.Timers.Timer myTimer;

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            //If cancellation is pending, cancel work.  
            if (backgroundWorkerStopCheck.CancellationPending)
            {
                e.Cancel = true;
                return;
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            // Notify the backgroundWorker that the process is starting.
            backgroundWorkerStopCheck.RunWorkerAsync();
            LaunchCode();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void LaunchCode()
        {
            buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
            myTimer = new System.Timers.Timer(5000); // Waste five seconds.
            myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
            myTimer.Enabled = true; // Start the timer.
        }

        void myTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
        }
    }
}

如果代码运行正常,它会在用户单击“开始”后停留五秒钟,然后重新启用“开始”按钮,或者如果用户单击“停止”,则会快速重新激活“开始”按钮。

这段代码有两个问题我不确定如何处理:

1) “myTimer_Elapsed”方法在尝试启用“开始”按钮时导致 InvalidOperationException,因为“跨线程操作无效”。如何避免跨线程操作?

2) 现在 backgroundWorker 没有完成任何事情,因为我不知道如何向它提供参数,以便在它被取消时停止计时器。

如有任何帮助,我将不胜感激!

最佳答案

首先,避免“跨线程操作无效”的问题是使用Invoke在控件上。您不能使用来自不同线程的控件。

关于第二个问题,我会按照下面的方式来实现。这是具有取消支持的最小后台工作程序实现。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication5
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            // Set the background worker to allow the user to stop the process. 
            backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
            backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
        }

        private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
        {
            try
            {
                for (int i = 0; i < 50; i++)
                {
                    if (backgroundWorkerStopCheck.CancellationPending)
                    {
                        // user cancel request
                        e.Cancel = true;
                        return;
                    }

                    System.Threading.Thread.Sleep(100);
                }
            }
            finally
            {
                InvokeEnableStartButton();
            }
        }

        private void buttonStart_Click(object sender, EventArgs e)
        {
            //disable start button before launch work
            buttonStart.Enabled = false;

            // start worker
            backgroundWorkerStopCheck.RunWorkerAsync();
        }

        private void buttonStop_Click(object sender, EventArgs e)
        {
            // Tell the backgroundWorker to stop process.
            backgroundWorkerStopCheck.CancelAsync();
        }

        private void InvokeEnableStartButton()
        {
            // this method is called from a thread,
            // we need to Invoke to avoid "cross thread exception"
            if (this.InvokeRequired)
            {
                this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
            }
            else
            {
                EnableStartButton();
            }
        }

        private void EnableStartButton()
        {
            buttonStart.Enabled = true;
        }
    }

    internal delegate void EnableStartButtonDelegate();
}

关于向 worker 传递参数,您可以在 RunWorkerAsync() 方法中传递任何对象,并在 backgroundWorkerStopCheck_DoWork 方法中接收它:

  ...
  backgroundWorkerStopCheck.RunWorkerAsync("hello");
  ...

  private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
  {
      string argument = e.Argument as string;
      // argument value is "hello"
      ...
  }

希望对您有所帮助。

关于c# - 将参数传递给 backgroundWorker(用作取消按钮),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8155166/

相关文章:

c# - Windows Vista\7 上的应用程序 list 、管理员权限和自动启动

c# - 将 Excel 中的行转换为自定义实体

C#,后台 worker

c# - 当后台 worker 到达 "...Completed"事件处理程序时可以继续工作吗

c# - 后台工作人员仍然忙碌

c# - 在 GridView asp.net 中显示文件名

c# - wcf 服务服务器和客户端证书与 SSl 证书不同时的安全异常

multithreading - Scala阻塞队列,进行适当的等待

java - 多线程简单死锁错误逃避检测

ios - 将 NSPrivateQueueConcurrencyType 子上下文与 NSPrivateQueueConcurrencyType 父上下文一起使用