Domanda

Prendi in considerazione un metodo ipotetico di un oggetto che fa cose per te:

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

    ...

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

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

Come si può attendere l'esecuzione di un BackgroundWorker?


In passato le persone hanno provato:

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

Ma questo deadlock , perché IsBusy non viene cancellato fino a quando non viene gestito l'evento RunWorkerCompleted e tale evento non può essere gestito fino a quando l'applicazione diventa inattiva. L'applicazione non diventerà inattiva fino a quando il lavoratore non avrà terminato. (Inoltre, è un circuito affollato - disgustoso.)

Altri hanno aggiunto di aver suggerito di includerlo in:

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

Il problema è che Application.DoEvents () causa l'elaborazione dei messaggi attualmente in coda, che causano problemi di rientro (.NET non rientra).

Spero di usare una soluzione che coinvolga gli oggetti di sincronizzazione degli eventi, in cui il codice attende un evento - impostato dai gestori di eventi RunWorkerCompleted del lavoratore. Qualcosa del tipo:

Event _workerDoneEvent = new WaitHandle();

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

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

Ma sono tornato allo stallo: il gestore di eventi non può essere eseguito fino a quando l'applicazione non è inattiva e l'applicazione non sarà inattiva perché è in attesa di un evento.

Quindi, come puoi aspettare che un BackgroundWorker finisca?


Aggiorna Le persone sembrano essere confuse da questa domanda. Sembrano pensare che userò BackgroundWorker come:

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

Questo è non , cioè non quello che sto facendo, e cioè non ciò che viene chiesto qui. Se così fosse, non avrebbe senso utilizzare un lavoratore in background.

È stato utile?

Soluzione

Se capisco bene il tuo requisito, potresti fare qualcosa del genere (codice non testato, ma mostra l'idea generale):

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
}

Altri suggerimenti

Si è verificato un problema con this risposta. L'interfaccia utente deve continuare a elaborare i messaggi durante l'attesa, altrimenti non verrà ridipinta, il che sarà un problema se il lavoratore in background impiega molto tempo a rispondere alla richiesta di annullamento.

Un secondo difetto è che _resetEvent.Set () non verrà mai chiamato se il thread di lavoro genera un'eccezione - lasciando il thread principale in attesa indefinitamente - tuttavia questo difetto potrebbe essere facilmente risolto con un tentativo / infine bloccare.

Un modo per farlo è quello di visualizzare una finestra di dialogo modale che ha un timer che controlla ripetutamente se il lavoratore in background ha terminato il lavoro (o ha terminato l'annullamento nel tuo caso). Una volta terminato il lavoratore in background, la finestra di dialogo modale restituisce il controllo all'applicazione. L'utente non può interagire con l'interfaccia utente fino a quando ciò non accade.

Un altro metodo (supponendo che sia aperto un massimo di una finestra non modale) è impostare ActiveForm.Enabled = false, quindi eseguire il ciclo su Applicazione, DoEvents fino a quando il lavoratore in background ha terminato l'annullamento, dopodiché è possibile impostare ActiveForm.Enabled = true ancora una volta.

Quasi tutti voi siete confusi dalla domanda e non capite come viene utilizzato un lavoratore.

Prendi in considerazione un gestore eventi RunWorkerComplete:

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

E tutto va bene.

Ora arriva una situazione in cui il chiamante deve interrompere il conto alla rovescia perché deve eseguire un'autodistruzione di emergenza del razzo.

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

    StartClaxon();
    SelfDestruct();
}

E c'è anche una situazione in cui dobbiamo aprire le porte di accesso al razzo, ma non mentre facciamo un conto alla rovescia:

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

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

E infine, dobbiamo scaricare il razzo, ma ciò non è consentito durante un conto alla rovescia:

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

    if (rocketOnPad)
        OpenFuelValves();
}

Senza la possibilità di attendere l'annullamento di un lavoratore, è necessario spostare tutti e tre i metodi in 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();
}

Ora potrei scrivere il mio codice in questo modo, ma non lo farò. Non mi interessa, non lo sono.

Puoi accedere a RunWorkerCompletedEventArgs in RunWorkerCompletedEventHandler per vedere qual era lo stato. Esito positivo, annullato o errore.

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

Aggiorna : per vedere se il tuo lavoratore ha chiamato .CancelAsync () usando questo:

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

Non non attendere il completamento del lavoratore in background. Questo praticamente vanifica lo scopo di lanciare un thread separato. Invece, dovresti completare il tuo metodo e spostare qualsiasi codice che dipende dal completamento in un altro posto. Lascia che il lavoratore ti dica quando ha finito e chiama qualsiasi codice rimanente.

Se vuoi attendere per completare qualcosa usa un diverso costrutto di threading che fornisce WaitHandle.

Perché non puoi semplicemente collegarti all'evento BackgroundWorker.RunWorkerCompleted. È un callback che si verificherà quando l'operazione in background è stata completata, è stata annullata o ha sollevato un'eccezione. & Quot;

Non capisco perché vorresti attendere il completamento di un BackgroundWorker; sembra davvero l'esatto contrario della motivazione per la classe.

Tuttavia, è possibile avviare tutti i metodi con una chiamata a worker.IsBusy e farli uscire se è in esecuzione.

Voglio solo dire che sono venuto qui perché ho bisogno di un lavoratore in background per aspettare mentre stavo eseguendo un processo asincrono mentre in un ciclo, la mia correzione era molto più semplice di tutte queste altre cose ^^

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

Ho solo pensato di condividere perché è qui che sono finito mentre cercavo una soluzione. Inoltre, questo è il mio primo post su overflow dello stack, quindi se è cattivo o qualcosa che amerei i critici! :)

Forse non sto capendo bene la tua domanda.

Il lavoratore in background chiama l'evento WorkerCompleted una volta il suo 'metodo di lavoro' (il metodo / funzione / sub che gestisce backgroundworker.doWork-event ) è terminato, quindi non è necessario verificare se il BW è ancora in esecuzione. Se vuoi fermare il tuo lavoratore, controlla cancellazione in attesa di proprietà all'interno del tuo" metodo lavoratore ".

Il flusso di lavoro di un oggetto BackgroundWorker richiede sostanzialmente di gestire l'evento RunWorkerCompleted sia per i casi d'uso di normale esecuzione che di cancellazione dell'utente. Questo è il motivo per cui esiste la proprietà RunWorkerCompletedEventArgs.Cancelled . Fondamentalmente, farlo correttamente richiede di considerare il metodo Cancel come un metodo asincrono in sé.

Ecco un esempio:

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

Se davvero davvero non vuoi che il tuo metodo esca, ti suggerirei di mettere un flag come un AutoResetEvent su un BackgroundWorker , quindi sovrascrivi OnRunWorkerCompleted per impostare il flag. È comunque un po 'kludgy; Consiglio di trattare l'evento di annullamento come un metodo asincrono e di fare qualunque cosa stia facendo attualmente nel gestore RunWorkerCompleted .

Sono un po 'in ritardo alla festa qui (circa 4 anni) ma per quanto riguarda l'impostazione di un thread asincrono in grado di gestire un loop occupato senza bloccare l'interfaccia utente, quindi avere il callback da quel thread essere la conferma che BackgroundWorker ha finito di annullare?

Qualcosa del genere:

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

In sostanza, ciò che fa è lanciare un altro thread da eseguire in background che attende solo nel suo ciclo occupato per vedere se il MyWorker è stato completato. Una volta che MyWorker ha terminato l'annullamento del thread verrà chiuso e possiamo utilizzarlo AsyncCallback per eseguire qualsiasi metodo di cui abbiamo bisogno per seguire la cancellazione riuscita - funzionerà come un evento psuedo . Poiché questo è separato dal thread dell'interfaccia utente, non bloccherà l'interfaccia utente mentre aspettiamo che MyWorker termini l'annullamento. Se la tua intenzione è davvero quella di bloccare e attendere l'annullamento, questo è inutile per te, ma se vuoi solo aspettare in modo da poter avviare un altro processo, allora funziona perfettamente.

So che è molto tardi (5 anni) ma quello che stai cercando è usare un thread e un SynchronizationContext . Dovrai eseguire il marshalling delle chiamate dell'interfaccia utente al thread dell'interfaccia utente "a mano". piuttosto che lasciare che il Framework lo faccia automaticamente per magia.

Questo ti permette di usare un thread che puoi aspettare se necessario.

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

La soluzione di Fredrik Kalseth a questo problema è la migliore che ho trovato finora. Altre soluzioni utilizzano Application.DoEvent () che può causare problemi o semplicemente non funzionare. Consentitemi di gettare la sua soluzione in una classe riutilizzabile. Poiché BackgroundWorker non è sigillato, possiamo derivarne la nostra classe:

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 le bandiere e il corretto blocco, ci assicuriamo che _resetEvent.WaitOne () venga davvero chiamato solo se è stato avviato qualche lavoro, altrimenti _resetEvent.Set (); potrebbe non essere mai stato chiamato!

Il try-finally garantisce che verrà chiamato _resetEvent.Set (); , anche se nel nostro gestore DoWork dovesse verificarsi un'eccezione. Altrimenti l'applicazione potrebbe bloccarsi per sempre quando si chiama CancelSync !

Lo useremmo così:

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

Puoi anche aggiungere un gestore all'evento RunWorkerCompleted come mostrato qui:
& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; BackgroundWorker Class (documentazione Microsoft) .

La chiusura del modulo chiude il mio file di registro aperto. Il mio lavoratore in background scrive quel file di registro, quindi non posso lasciare che MainWin_FormClosing () finisca fino a quando il mio lavoratore in background non termina. Se non aspetto il termine del mio lavoratore in background, si verificano eccezioni.

Perché è così difficile?

Un semplice Thread.Sleep (1500) funziona, ma ritarda l'arresto (se troppo lungo) o provoca eccezioni (se troppo breve).

Per chiudere subito dopo il termine del lavoratore in background, basta usare una variabile. Questo funziona per me:

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.)

È possibile tornare indietro dall'evento RunWorkerCompleted. Anche se hai già aggiunto un gestore eventi per _worker, puoi aggiungerne un altro che eseguiranno nell'ordine in cui sono stati aggiunti.

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

questo potrebbe essere utile se hai diversi motivi per cui può verificarsi un annullamento, rendendo la logica di un singolo gestore RunWorkerCompleted più complicata di quanto desideri. Ad esempio, annullando quando un utente tenta di chiudere il modulo:

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

Uso il metodo async e wait per attendere che il lavoratore finisca il suo lavoro:

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

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

e nel metodo DoWork :

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

Puoi anche incapsulare il mentre il ciclo in DoWork con prova ... cattura per impostare _isBusy è false su eccezione. Oppure, seleziona _worker.IsBusy in StopAsync durante il ciclo.

Ecco un esempio di piena attuazione:

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

Per interrompere il lavoratore e attendere fino alla fine:

await myBackgroundWorker.StopAsync();

I problemi con questo metodo sono:

  1. Devi usare i metodi asincroni fino in fondo.
  2. attende Task.Delay non è preciso. Sul mio PC, Task.Delay (1) in realtà attende ~ 20ms.

oh amico, alcuni di questi sono diventati ridicolmente complessi. tutto quello che devi fare è controllare la proprietà BackgroundWorker.CancellationPending all'interno del gestore DoWork. puoi verificarlo in qualsiasi momento. una volta in sospeso, imposta e.Cancel = True e salvi dal metodo.

// metodo qui void privato Worker_DoWork (mittente oggetto, DoWorkEventArgs e) {     BackgroundWorker bw = (mittente come BackgroundWorker);

// do stuff

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

// do other stuff

}

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top