-2

i want to just stop my backgroundworker when i press a button : Code looking like :

Button :

 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();

            }

And my Backgroundworker_DoWork look like :

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



    }

And the problem is : I can't .CancelAsync(); just immediately after button press again . My code just DoWork untill just Thread.Sleep(4000); is over. When i press my button to stop work this gonna stop just after end while loop.

I know i can add

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

After everyline in my Backgroundworker_DoWork but it's so stupid and when i get Thread.Sleep(10000); it gonna takes 10 sec... Is any way to just kill instantly my background worker? Thanks for help!

Adam Zbudniewek
  • 107
  • 2
  • 12
  • Set cancellation token and "enable" it when need – J. Doe Mar 09 '17 at 15:10
  • 2
    This question has been asked so many times. Please show evidence that you researched this before asking it here. – rory.ap Mar 09 '17 at 15:10
  • 1
    Don't create a new thread just so you can have it sit there doing nothing. If you don't have CPU bound work to do, don't use a BGW. – Servy Mar 09 '17 at 15:13
  • 1
    So, you're just sleeping you worker and want to interrupt its sleep to cancel it? Is this code supposed to be practical? – Broots Waymb Mar 09 '17 at 15:14
  • This is just only example i want to immediately kill this worker – Adam Zbudniewek Mar 09 '17 at 15:18
  • Would be good if the c# language upon detecting a novice trying to use threads, automatically ban the use of `Thread.Sleep()`. ;) –  Mar 09 '17 at 15:19
  • 1
    This is not how task cancellation works, you don't just kill it dead. It's co-operative, the method checks at key points to see if it needs to exit and does so gracefully when appropriate. If it's busy executing a `Thread.Sleep(x)` then it can't check the token until this is finished, so yes you'll have to wait, this is intended. What if you were writing to a database and bombed out half way through? You'd be in bad shape. Instead it would exit _after_ writing to the database... – Equalsk Mar 09 '17 at 15:30
  • So what can i do to just exit immediately of my background worker ? any example or something – Adam Zbudniewek Mar 09 '17 at 15:36
  • Derive your own backgroundworker as shown here: http://stackoverflow.com/questions/800767/how-to-kill-background-worker-completely – Equalsk Mar 09 '17 at 15:42
  • @ yes but this don't work for me – Adam Zbudniewek Mar 09 '17 at 16:06
  • @Adam, this looks like a [XY Problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem) Tell us what you are really trying to do and then we'll be able to come up with a solution for your problem. – SergGr Mar 09 '17 at 16:12
  • @SergGr , I try to do so: when I click button , BackgroundWorker do your code, and when I click the button again (immediately effectively stopped the code from) the Backgroundworker. For example : When i have Thread.Sleep(10000) inside my backgroundworker and i press button for example every 2 sec this gonna just wait 8 sec till this Thread.Sleep(10000) gonna end. All i need to do is just end background worker when i click button(immediately) don't want just wait this 8 sec, nothing more. – Adam Zbudniewek Mar 09 '17 at 16:22
  • @Adam, Sorry, you are again restating problems with your **solution** not your real problem. Real problem description should provide answers to questions such as why you need `Thread.Sleep` and why you need immediate cancellation at all. – SergGr Mar 09 '17 at 16:24
  • @SergGr I edied my main topic , and changed code of :: And my Backgroundworker_DoWork look like : . And i just have Thread.Sleep in my BackgroundWorker it's important you see? And i explain @ up what i want to do – Adam Zbudniewek Mar 09 '17 at 16:32

1 Answers1

0

I think that standard BackgroundWorker is not suitable for your case and you should do something custom that better support combination of sleep and cancellation. Following code is an idea of what you might want to do:

CancellableBackgroundWorker.cs

This is a class similar to standard BackgroundWorker but providing some callbacks for your goal (see ICancellationProvider and 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

}

The main trick is to use ManualResetEvent.WaitOne instead of Thread.Sleep for sleeping. With such approach working thread might be safely woken up (for cancellation) from a different (UI) thread. Another trick is to use ThreadAbortException via Thread.Abort to enforce quick end of the background thread execution (and don't forget about Thread.ResetAbort at the end of stack unwinding).

You may use this class as following:

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");
    }
}
SergGr
  • 23,570
  • 2
  • 30
  • 51