Pregunta

Considere la posibilidad de un hipotético método de un objeto que hace cosas para usted:

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.CancelAsync();

        //todo: Figure out a way to wait for BackgroundWorker to be cancelled.
    }
}

Cómo puede uno esperar de un BackgroundWorker a hacer?


En el pasado, la gente ha tratado de:

while (_worker.IsBusy)
{
    Sleep(100);
}

Pero este interbloqueos, porque IsBusy no se borra hasta después de la RunWorkerCompleted evento que se maneja, y que el evento no puede ser manejado hasta que la aplicación va inactivo.La aplicación no ir inactivo hasta que el trabajo está hecho.(Además, es un bucle ocupado - un asco.)

Otros han agregar sugirió kludging en:

while (_worker.IsBusy)
{
    Application.DoEvents();
}

El problema con eso es que es Application.DoEvents() hace que los mensajes actualmente en la cola para ser procesados, lo que causa re-entrancy problemas (.NET no es re-participante).

Tengo la esperanza de utilizar alguna solución con sincronización de Eventos de objetos, donde el código espera para el caso de que el trabajador RunWorkerCompleted los controladores de eventos conjuntos.Algo así como:

Event _workerDoneEvent = new WaitHandle();

public void CancelDoingStuff()
{
    _worker.CancelAsync();
    _workerDoneEvent.WaitOne();
}

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    _workerDoneEvent.SetEvent();
}

Pero estoy de vuelta para el interbloqueo:el controlador de eventos no se puede ejecutar hasta que la aplicación va inactivo, y la aplicación no ir inactivo porque es la espera de un Evento.

Entonces, ¿cómo puede usted esperar de un BackgroundWorker para terminar?


Actualización La gente parece estar confundido por esta pregunta.Ellos parecen pensar que voy a estar usando el BackgroundWorker como:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);

Que es no ella, que es no lo que yo estoy haciendo, y que es no lo que se pregunta aquí.Si ese fuera el caso, no habría ningún punto en el uso de un trabajo en segundo plano.

¿Fue útil?

Solución

Si entiendo su requisito, usted podría hacer algo como esto (el código no probado, pero muestra la idea general):

private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);

public Form1()
{
    InitializeComponent();

    worker.DoWork += worker_DoWork;
}

public void Cancel()
{
    worker.CancelAsync();
    _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while(!e.Cancel)
    {
        // do something
    }

    _resetEvent.Set(); // signal that worker is done
}

Otros consejos

Hay un problema con este respuesta.La interfaz de usuario debe seguir para procesar los mensajes mientras están a la espera, de lo contrario no se vuelva a pintar, que va a ser un problema si su fondo trabajador se toma un largo tiempo para responder a la petición de cancelar.

Un segundo defecto es que _resetEvent.Set() nunca será llamada si el subproceso de trabajo lanza una excepción - dejando el hilo principal esperando indefinidamente - sin embargo, esta falla puede solucionar fácilmente con un try/finally bloque.

Una manera de hacer esto es para mostrar un cuadro de diálogo modal que tiene un temporizador que en repetidas ocasiones se comprueba si el fondo trabajador ha finalizado el trabajo (o terminado de cancelar en su caso).Una vez que el fondo trabajador ha terminado, el cuadro de diálogo modal devuelve el control a la aplicación.El usuario no puede interactuar con la interfaz de usuario hasta que esto suceda.

Otro método (suponiendo que se tiene un máximo de una ventana no modal abierto) es establecer ActiveForm.Enabled = false, entonces el bucle de Aplicación,DoEvents hasta el fondo trabajador ha terminado de cancelar, después de lo cual usted puede establecer ActiveForm.Enabled = true nuevo.

Casi todas de que usted está confundido por la pregunta, y no la comprensión de cómo un trabajador se utiliza.

Considere la posibilidad de un RunWorkerComplete controlador de eventos:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;
}

Y todo es bueno.

Ahora viene una situación en la que la persona necesita para cancelar la cuenta atrás, porque ellos necesitan para ejecutar una emergencia de autodestrucción del cohete.

private void BlowUpRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    StartClaxon();
    SelfDestruct();
}

Y también hay una situación en la que tenemos que abrir las puertas de acceso a la nave, pero no mientras se hace una cuenta regresiva:

private void OpenAccessGates()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

Y por último, tenemos que de-el combustible del cohete, pero que no está permitido durante la cuenta regresiva:

private void DrainRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

Sin la capacidad de esperar para un trabajador a cancelar, debemos mover todos los tres métodos para el RunWorkerCompletedEvent:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;

    if (delayedBlowUpRocket)
        BlowUpRocket();
    else if (delayedOpenAccessGates)
        OpenAccessGates();
    else if (delayedDrainRocket)
        DrainRocket();
}

private void BlowUpRocket()
{
    if (worker != null)
    {
        delayedBlowUpRocket = true;
        worker.CancelAsync();
        return;
    }

    StartClaxon();
    SelfDestruct();
}

private void OpenAccessGates()
{
    if (worker != null)
    {
        delayedOpenAccessGates = true;
        worker.CancelAsync();
        return;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

private void DrainRocket()
{
    if (worker != null)
    {
        delayedDrainRocket = true;
        worker.CancelAsync();
        return;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

Ahora puedo escribir mi código como ese, pero yo creo que no va a.No me importa, yo no soy así.

Se puede comprobar en el RunWorkerCompletedEventArgs en el RunWorkerCompletedEventHandler a ver cuál es el estado.Éxito, cancelado o un error.

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
    {
        Console.WriteLine("The worker was cancelled.");
    }
}

Actualización:A ver si el trabajador ha llamado .CancelAsync() mediante el uso de este:

if (_worker.CancellationPending)
{
    Console.WriteLine("Cancellation is pending, no need to call CancelAsync again");
}

Usted no espere a que el trabajo en segundo plano para completar.Que bastante derrotas el propósito de lanzar un hilo separado.En su lugar, usted debe dejar a su método de acabado, y mover cualquier código que depende de la finalización de un lugar diferente.Deja que el trabajador indique cuándo es hecho y llamar a cualquier resto de código, a continuación,.

Si quieres esperar para algo para completar el uso de diferentes roscado de la construcción que ofrece un WaitHandle.

¿Por qué no puede atar en el BackgroundWorker.RunWorkerCompleted Evento.Es una devolución de llamada que se "produce cuando el fondo de la operación se ha completado, ha sido cancelada, o haya hecho una excepción."

No entiendo por qué te gustaría esperar un BackgroundWorker para completar;realmente parece el opuesto exacto de la motivación para la clase.

Sin embargo, usted podría comenzar cada método con un llamamiento a los trabajadores.IsBusy y ellos tienen salida si se está ejecutando.

Sólo quiero decir que he venido aquí porque necesito un trabajo en segundo plano a esperar mientras yo estaba corriendo un async proceso, mientras que en un bucle, mi solución fue la manera más fácil de todas estas otras cosas^^

foreach(DataRow rw in dt.Rows)
{
     //loop code
     while(!backgroundWorker1.IsBusy)
     {
         backgroundWorker1.RunWorkerAsync();
     }
}

Sólo pensé en compartir porque aquí es donde terminé mientras que la búsqueda de una solución.También, este es mi primer post sobre el desbordamiento de la pila de modo que si es malo o cualquier cosa que me encantaría críticos!:)

Hm tal vez no estoy llegando a tu pregunta.

El backgroundworker llama a la WorkerCompleted evento una vez que su 'workermethod' (el método/función/sub que se encarga de la backgroundworker.doWork-evento se termina así que no hay necesidad de comprobar si el BW todavía se está ejecutando.Si usted quiere dejar a su trabajador de verificación de la cancelación pendiente de propiedad en el interior de su trabajador, y el método".

El flujo de trabajo de un BackgroundWorker objeto básicamente requiere para manejar el RunWorkerCompleted evento para la normal ejecución de usuario y la cancelación de los casos de uso.Esta es la razón por la propiedad RunWorkerCompletedEventArgs.Cancelada existe.Básicamente, hacer esto correctamente es necesario tener en cuenta su método Cancel para ser un método asincrónico en sí mismo.

He aquí un ejemplo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    public class AsyncForm : Form
    {
        private Button _startButton;
        private Label _statusLabel;
        private Button _stopButton;
        private MyWorker _worker;

        public AsyncForm()
        {
            var layoutPanel = new TableLayoutPanel();
            layoutPanel.Dock = DockStyle.Fill;
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

            _statusLabel = new Label();
            _statusLabel.Text = "Idle.";
            layoutPanel.Controls.Add(_statusLabel, 0, 0);

            _startButton = new Button();
            _startButton.Text = "Start";
            _startButton.Click += HandleStartButton;
            layoutPanel.Controls.Add(_startButton, 0, 1);

            _stopButton = new Button();
            _stopButton.Enabled = false;
            _stopButton.Text = "Stop";
            _stopButton.Click += HandleStopButton;
            layoutPanel.Controls.Add(_stopButton, 1, 1);

            this.Controls.Add(layoutPanel);
        }

        private void HandleStartButton(object sender, EventArgs e)
        {
            _stopButton.Enabled = true;
            _startButton.Enabled = false;

            _worker = new MyWorker() { WorkerSupportsCancellation = true };
            _worker.RunWorkerCompleted += HandleWorkerCompleted;
            _worker.RunWorkerAsync();

            _statusLabel.Text = "Running...";
        }

        private void HandleStopButton(object sender, EventArgs e)
        {
            _worker.CancelAsync();
            _statusLabel.Text = "Cancelling...";
        }

        private void HandleWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                _statusLabel.Text = "Cancelled!";
            }
            else
            {
                _statusLabel.Text = "Completed.";
            }

            _stopButton.Enabled = false;
            _startButton.Enabled = true;
        }

    }

    public class MyWorker : BackgroundWorker
    {
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            base.OnDoWork(e);

            for (int i = 0; i < 10; i++)
            {
                System.Threading.Thread.Sleep(500);

                if (this.CancellationPending)
                {
                    e.Cancel = true;
                    e.Result = false;
                    return;
                }
            }

            e.Result = true;
        }
    }
}

Si realmente no quiere que su método para salir, te sugiero poner una bandera como un AutoResetEvent en un derivado de BackgroundWorker, a continuación, reemplazar OnRunWorkerCompleted para establecer la bandera.Sigue siendo una especie de desacoplados aunque;Me gustaría recomendar el tratamiento de cancelar el evento como un método asincrónico y hacer lo que se está haciendo actualmente en el RunWorkerCompleted controlador.

Estoy un poco tarde a la fiesta, aquí (unos 4 años) pero, ¿qué acerca de la configuración de una asincronía en el hilo que puede manejar un bucle ocupado sin bloqueo de la interfaz de usuario, entonces la devolución de llamada a partir de que el hilo sea la confirmación de que el BackgroundWorker ha terminado de cancelar?

Algo como esto:

class Test : Form
{
    private BackgroundWorker MyWorker = new BackgroundWorker();

    public Test() {
        MyWorker.DoWork += new DoWorkEventHandler(MyWorker_DoWork);
    }

    void MyWorker_DoWork(object sender, DoWorkEventArgs e) {
        for (int i = 0; i < 100; i++) {
            //Do stuff here
            System.Threading.Thread.Sleep((new Random()).Next(0, 1000));  //WARN: Artificial latency here
            if (MyWorker.CancellationPending) { return; } //Bail out if MyWorker is cancelled
        }
    }

    public void CancelWorker() {
        if (MyWorker != null && MyWorker.IsBusy) {
            MyWorker.CancelAsync();
            System.Threading.ThreadStart WaitThread = new System.Threading.ThreadStart(delegate() {
                while (MyWorker.IsBusy) {
                    System.Threading.Thread.Sleep(100);
                }
            });
            WaitThread.BeginInvoke(a => {
                Invoke((MethodInvoker)delegate() { //Invoke your StuffAfterCancellation call back onto the UI thread
                    StuffAfterCancellation();
                });
            }, null);
        } else {
            StuffAfterCancellation();
        }
    }

    private void StuffAfterCancellation() {
        //Things to do after MyWorker is cancelled
    }
}

En esencia lo que hace es disparar otro hilo para que se ejecute en segundo plano que sólo espera en bucle ocupado para ver si el MyWorker se ha completado.Una vez MyWorker ha terminado de cancelar el hilo va de salida y que podemos utilizar es AsyncCallback para ejecutar cualquiera sea el método que debemos seguir el éxito de la cancelación - funcionará como un pseudo-evento.Ya que esto es independiente del subproceso de interfaz de usuario no va a bloquear la interfaz de usuario, mientras esperamos MyWorker para terminar de cancelar.Si su intención es realmente para bloquear y esperar a que el cancelar, a continuación, esto es inútil para usted, pero si usted sólo quiere esperar para que usted pueda iniciar otro proceso, entonces esto funciona muy bien.

Sé que esto es muy tarde (5 años), pero lo que se busca es utilizar un Hilo y un SynchronizationContext.Usted va a tener que calcular la interfaz de usuario de las llamadas para el subproceso de interfaz de usuario "a mano" en lugar de dejar que el Marco hacerlo auto-mágicamente.

Esto le permite utilizar un Hilo que se puede Esperar si es necesario.

Imports System.Net
Imports System.IO
Imports System.Text

Public Class Form1
   Dim f As New Windows.Forms.Form
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   BackgroundWorker1.WorkerReportsProgress = True
    BackgroundWorker1.RunWorkerAsync()
    Dim l As New Label
    l.Text = "Please Wait"
    f.Controls.Add(l)
    l.Dock = DockStyle.Fill
    f.StartPosition = FormStartPosition.CenterScreen
    f.FormBorderStyle = Windows.Forms.FormBorderStyle.None
    While BackgroundWorker1.IsBusy
        f.ShowDialog()
    End While
End Sub




Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    Dim i As Integer
    For i = 1 To 5
        Threading.Thread.Sleep(5000)
        BackgroundWorker1.ReportProgress((i / 5) * 100)
    Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    Me.Text = e.ProgressPercentage

End Sub

 Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

    f.Close()

End Sub

End Class

Fredrik Kalseth la solución a este problema es la mejor que he encontrado hasta ahora.Otras soluciones de uso Application.DoEvent() que pueden causar problemas o simplemente no funcionan.Permítanme emitir su solución en una clase reutilizable.Desde BackgroundWorker no está sellado, podemos derivar nuestra clase de ella:

public class BackgroundWorkerEx : BackgroundWorker
{
    private AutoResetEvent _resetEvent = new AutoResetEvent(false);
    private bool _resetting, _started;
    private object _lockObject = new object();

    public void CancelSync()
    {
        bool doReset = false;
        lock (_lockObject) {
            if (_started && !_resetting) {
                _resetting = true;
                doReset = true;
            }
        }
        if (doReset) {
            CancelAsync();
            _resetEvent.WaitOne();
            lock (_lockObject) {
                _started = false;
                _resetting = false;
            }
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        lock (_lockObject) {
            _resetting = false;
            _started = true;
            _resetEvent.Reset();
        }
        try {
            base.OnDoWork(e);
        } finally {
            _resetEvent.Set();
        }
    }
}

Con banderas y de bloqueo adecuados, nos aseguramos de que _resetEvent.WaitOne() realmente se llama sólo si algunos se han iniciado trabajos, de lo contrario _resetEvent.Set(); puede que nunca ha llamado!

El try-finally se asegura de que _resetEvent.Set(); serán llamados, incluso si una excepción debe ocurrir en nuestro DoWork-controlador.De lo contrario, la aplicación podría congelar para siempre cuando se llama a CancelSync!

Utilizaríamos como este:

BackgroundWorkerEx _worker;

void StartWork()
{
    StopWork();
    _worker = new BackgroundWorkerEx { 
        WorkerSupportsCancellation = true,
        WorkerReportsProgress = true
    };
    _worker.DoWork += Worker_DoWork;
    _worker.ProgressChanged += Worker_ProgressChanged;
}

void StopWork()
{
    if (_worker != null) {
        _worker.CancelSync(); // Use our new method.
    }
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 1; i <= 20; i++) {
        if (worker.CancellationPending) {
            e.Cancel = true;
            break;
        } else {
            // Simulate a time consuming operation.
            System.Threading.Thread.Sleep(500);
            worker.ReportProgress(5 * i);
        }
    }
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressLabel.Text = e.ProgressPercentage.ToString() + "%";
}

También puede agregar un controlador para el RunWorkerCompleted evento, como se muestra aquí:
     Clase BackgroundWorker (La documentación de Microsoft).

Cierre el formulario se cierra mi abrir el archivo de registro.Mi trabajo en segundo plano escribe que el archivo de registro, por lo que no puedo dejar MainWin_FormClosing() acabado hasta mi fondo trabajador termina.Si yo no espere a que mi trabajo en segundo plano para terminar, se producen excepciones.

¿Por qué es esto tan difícil?

Un simple Thread.Sleep(1500) funciona, pero los retrasos de apagado (si es demasiado larga), o causas excepciones (si es muy corto).

Para apagar la derecha después de que el fondo trabajador termina, sólo tiene que utilizar una variable.Este es trabajo para mí:

private volatile bool bwRunning = false;

...

private void MainWin_FormClosing(Object sender, FormClosingEventArgs e)
{
    ... // Clean house as-needed.

    bwInstance.CancelAsync();  // Flag background worker to stop.
    while (bwRunning)
        Thread.Sleep(100);  // Wait for background worker to stop.
}  // (The form really gets closed now.)

...

private void bwBody(object sender, DoWorkEventArgs e)
{
    bwRunning = true;

    BackgroundWorker bw = sender as BackgroundWorker;

    ... // Set up (open logfile, etc.)

    for (; ; )  // infinite loop
    {
        ...
        if (bw.CancellationPending) break;
        ...
    } 

    ... // Tear down (close logfile, etc.)

    bwRunning = false;
}  // (bwInstance dies now.)

Usted puede piggy back off de la RunWorkerCompleted evento.Incluso si ya has añadido un controlador de eventos para _worker, se puede añadir otra una que se ejecutan en el orden en que fueron añadidos.

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => 
        {
            // do whatever you want to do when the cancel completes in here!
        });
        _worker.CancelAsync();
    }
}

esto podría ser útil si tienes varias razones por las que una cancelar puede ocurrir, haciendo que la lógica de una sola RunWorkerCompleted controlador más complicado de lo que quieres.Por ejemplo, la cancelación cuando un usuario intenta cerrar el formulario:

void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (_worker != null)
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => this.Close());
        _worker.CancelAsync();
        e.Cancel = true;
    }
}

Yo uso async método y await a esperar por el trabajador al final de su trabajo:

    public async Task StopAsync()
    {
        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);
    }

y en DoWork método:

    public async Task DoWork()
    {
        _isBusy = true;
        while (!_worker.CancellationPending)
        {
            // Do something.
        }
        _isBusy = false;
    }

Usted también puede encapsular el while bucle en DoWork con try ... catch para establecer _isBusy es false en la excepción.O, simplemente marque _worker.IsBusy en el StopAsync bucle while.

Aquí es un ejemplo de plena aplicación:

class MyBackgroundWorker
{
    private BackgroundWorker _worker;
    private bool _isBusy;

    public void Start()
    {
        if (_isBusy)
            throw new InvalidOperationException("Cannot start as a background worker is already running.");

        InitialiseWorker();
        _worker.RunWorkerAsync();
    }

    public async Task StopAsync()
    {
        if (!_isBusy)
            throw new InvalidOperationException("Cannot stop as there is no running background worker.");

        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);

        _worker.Dispose();
    }

    private void InitialiseWorker()
    {
        _worker = new BackgroundWorker
        {
            WorkerSupportsCancellation = true
        };
        _worker.DoWork += WorkerDoWork;
    }

    private void WorkerDoWork(object sender, DoWorkEventArgs e)
    {
        _isBusy = true;
        try
        {
            while (!_worker.CancellationPending)
            {
                // Do something.
            }
        }
        catch
        {
            _isBusy = false;
            throw;
        }

        _isBusy = false;
    }
}

Para detener el trabajador y esperar a que se ejecuta hasta el final:

await myBackgroundWorker.StopAsync();

Los problemas con este método son:

  1. Usted tiene que utilizar los métodos de async todo el camino.
  2. esperan Tarea.La demora es inexacta.En mi PC, la Tarea.Delay(1) de hecho espera ~20ms.

oh hombre, algunos de ellos han conseguido ridículamente complejo.todo lo que usted necesita hacer es comprobar el BackgroundWorker.CancellationPending propiedad dentro de la DoWork controlador.usted puede comprobar en cualquier momento.una vez que está pendiente, conjunto de correo.Cancel = True y la libertad bajo fianza desde el método.

// método aquí private void Worker_DoWork(object sender, DoWorkEventArgs e) { BackgroundWorker bw = (remitente como BackgroundWorker);

// do stuff

if(bw.CancellationPending)
{
    e.Cancel = True;
    return;
}

// do other stuff

}

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top