后台工作人员 c# 立即停止工作人员(在 DoWork 中包含 Thread.Sleep)

Background worker c# stop worker - immediately (include Thread.Sleep in DoWork)

我只想在按下按钮时停止我的后台工作程序: 代码看起来像:

按钮:

 private void button6_Click(object sender, EventArgs e)
        {

            backgroundWorker1.WorkerReportsProgress = true;
            backgroundWorker1.WorkerSupportsCancellation = true;

            if (isOn == true)
            {
                isOn = false;

                if (!backgroundWorker1.IsBusy)
                {


                    backgroundWorker1.RunWorkerAsync();
                    this.button6.ForeColor = System.Drawing.Color.Lime;
                }
            }
            else
            {
                isOn = true;
                this.button6.ForeColor = System.Drawing.Color.Red;
                backgroundWorker1.CancelAsync();
                //////backgroundWorker1.Dispose();

            }

我的 Backgroundworker_DoWork 看起来像:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {

        if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
        {
            e.Cancel = true;
            return;
        }
        while (true)
        {

            if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
            {
                e.Cancel = true;
                break;
            }

            backgroundWorker1.Dispose();
                click_na_default(hwnd1);
                click_F8(hwnd1);
                click_na_YELLS(hwnd1);
                click_ENTER(hwnd1);
                Thread.Sleep(100);
                click_na_trade(hwnd1);
                Thread.Sleep(100);
                click_F8(hwnd1);
                click_ENTER(hwnd1);
                Thread.Sleep(100);
                click_na_default(hwnd1);
                Thread.Sleep(4000);

        }

        if (((BackgroundWorker)sender).CancellationPending)
        {
            e.Cancel = true;
            //set this code at the end of file processing
            return;
        }



    }

问题是:我无法在再次按下按钮后立即 .CancelAsync();。我的代码只是 DoWork 直到 Thread.Sleep(4000);结束了。 当我按下我的按钮停止工作时,它会在 while 循环结束后立即停止。

我知道我可以添加

 if (backgroundWorker1.CancellationPending && backgroundWorker1.IsBusy)
            {
                e.Cancel = true;
                return;
            }

在我 Backgroundworker_DoWork 的每一行之后,但它太愚蠢了,当我得到 Thread.Sleep(10000) 时;需要10秒... 有什么办法可以立即杀死我的后台工作人员吗? 感谢您的帮助!

我认为标准 BackgroundWorker 不适合您的情况,您应该做一些自定义以更好地支持睡眠和取消的组合。以下代码是您可能想要执行的操作的想法:

CancellableBackgroundWorker.cs

这是一个类似于标准 BackgroundWorker 的 class,但为您的目标提供了一些回调(参见 ICancellationProviderFinishedEvent)。

public delegate void CancellableBackgroundJob(ICancellationProvider cancellation);

public interface ICancellationProvider
{
    bool CheckForCancel();

    void CheckForCancelAndBreak();

    void SleepWithCancel(int millis);
}


public class CancellableBackgroundWorker : Component, ICancellationProvider
{
    private readonly ManualResetEvent _canceledEvent = new ManualResetEvent(false);
    private readonly CancellableBackgroundJob _backgroundJob;

    private volatile Thread _thread;
    private volatile bool _disposed;

    public EventHandler FinishedEvent;


    public CancellableBackgroundWorker(CancellableBackgroundJob backgroundJob)
    {
        _backgroundJob = backgroundJob;
    }

    protected override void Dispose(bool disposing)
    {
      Cancel();
      _disposed = true;
    }

    private void AssertNotDisposed()
    {
        if (_disposed)
            throw new InvalidOperationException("Worker is already disposed");
    }

    public bool IsBusy
    {
        get { return (_thread != null); }
    }

    public void Start()
    {
        AssertNotDisposed();
        if (_thread != null)
            throw new InvalidOperationException("Worker is already started");
        _thread = new Thread(DoWorkWrapper);
        _thread.Start();
    }


    public void Cancel()
    {
        AssertNotDisposed();
        _canceledEvent.Set();
    }

    private void DoWorkWrapper()
    {
        _canceledEvent.Reset();
        try
        {
            _backgroundJob(this);

            Debug.WriteLine("Worker thread completed successfully");
        }
        catch (ThreadAbortException ex)
        {
            Debug.WriteLine("Worker thread was aborted");
            Thread.ResetAbort();
        }
        finally
        {
            _canceledEvent.Reset();
            _thread = null;
            EventHandler finished = FinishedEvent;
            if (finished != null)
                finished(this, EventArgs.Empty);
        }
    }


    #region ICancellationProvider

    // use explicit implementation of the interface to separate interfaces
    // I'm too lazy to create additional class

    bool ICancellationProvider.CheckForCancel()
    {
        return _canceledEvent.WaitOne(0);
    }

    void ICancellationProvider.CheckForCancelAndBreak()
    {
        if (((ICancellationProvider)this).CheckForCancel())
        {
            Debug.WriteLine("Cancel event is set, aborting the worker thread");
            _thread.Abort();
        }
    }

    void ICancellationProvider.SleepWithCancel(int millis)
    {
        if (_canceledEvent.WaitOne(millis))
        {
            Debug.WriteLine("Sleep aborted by cancel event, aborting the worker thread");
            _thread.Abort();
        }
    }

    #endregion

}

主要技巧是使用 ManualResetEvent.WaitOne 而不是 Thread.Sleep 来睡觉。使用这种方法,可以从不同的 (UI) 线程安全地唤醒(用于取消)工作线程。另一个技巧是通过 Thread.Abort 使用 ThreadAbortException 强制快速结束后台线程执行(并且不要忘记在堆栈展开结束时的 Thread.ResetAbort)。

您可以按如下方式使用此 class:

public partial class Form1 : Form
{
    private readonly CancellableBackgroundWorker _backgroundWorker;

    public Form1()
    {
        InitializeComponent();

        _backgroundWorker = new CancellableBackgroundWorker(DoBackgroundJob);
        _backgroundWorker.FinishedEvent += (s, e) => UpdateButton();

        // ensure this.components is created either by InitializeComponent or by us explicitly
        // so we can add _backgroundWorker to it for disposal
        if (this.components == null)
            this.components = new System.ComponentModel.Container();

        components.Add(_backgroundWorker);
    }

    private void UpdateButton()
    {
        // Ensure we interact with UI on the main thread
        if (InvokeRequired)
        {
            Invoke((Action)UpdateButton);
            return;
        }

        button1.Text = _backgroundWorker.IsBusy ? "Cancel" : "Start";
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (_backgroundWorker.IsBusy)
        {
            _backgroundWorker.Cancel();
        }
        else
        {
            _backgroundWorker.Start();
        }
        UpdateButton();
    }

    private void DoBackgroundJob(ICancellationProvider cancellation)
    {
        Debug.WriteLine("Do something");

        // if canceled, stop immediately
        cancellation.CheckForCancelAndBreak();

        Debug.WriteLine("Do something more");
        if (cancellation.CheckForCancel())
        {
            // you noticed cancellation but still need to finish something
            Debug.WriteLine("Do some necessary clean up");
            return;
        }

        // Sleep but cancel will stop and break
        cancellation.SleepWithCancel(10000);
        Debug.WriteLine("Last bit of work");
    }
}