Фоновый работник C# остановите работника - немедленно (включая Thread.Sleep в 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); кончено. Когда я нажму кнопку, чтобы остановить работу, это остановится сразу после завершения цикла.

Я знаю, что могу добавить

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

После каждой строки в моем Backgroundworker_DoWork но это так глупо, и когда я получаю Thread.Sleep(10000); это займет 10 секунд... Есть ли способ просто убить мгновенно моего второстепенного работника? Спасибо за помощь!

1 ответ

Решение

Я думаю, что стандарт BackgroundWorker не подходит для вашего случая, и вы должны сделать что-то нестандартное, чтобы лучше поддерживать сочетание сна и отмены. Следующий код представляет собой идею того, что вы можете сделать:

CancellableBackgroundWorker.cs

Этот класс похож на стандарт BackgroundWorker но предоставляя некоторые обратные вызовы для вашей цели (см. ICancellationProvider а также FinishedEvent).

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 для сна. При таком подходе рабочий поток может быть безопасно выведен (для отмены) из другого потока (пользовательского интерфейса). Еще один трюк заключается в использовании 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");
    }
}
Другие вопросы по тегам