后台工作者c#stop worker - 立即(在DoWork中包含Thread.Sleep)

时间:2017-03-09 15:09:17

标签: c# backgroundworker

我想在按下按钮时停止我的背景工作: 代码看起来像:

按钮:

 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);结束了。 当我按下我的按钮停止工作时,这将在循环结束后停止。

我知道我可以添加

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

在我的Backgroundworker_DoWork中的每一行之后,但它太愚蠢了,当我得到Thread.Sleep(10000)时;它需要10秒...... 有没有办法立即杀死我的背景工作者? 谢谢你的帮助!

1 个答案:

答案 0 :(得分:1)

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

<强> CancellableBackgroundWorker.cs

这是一个类似于标准BackgroundWorker的类,但为您的目标提供了一些回调(请参阅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)线程安全地唤醒工作线程(用于取消)。另一个技巧是通过ThreadAbortException使用Thread.Abort来强制快速结束后台线程执行(并且在堆栈展开结束时不要忘记Thread.ResetAbort。)

您可以按以下方式使用此课程:

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");
    }
}