views:

93

answers:

2

Hello everybody!

I have UI which displaying status of long-running operations (downloading some text files from ftp) . For my purposes I use backgroundworker and I can't cancel operation.

void worker_DoWork( object sender, DoWorkEventArgs e )
    {

        try
        {
            int rowIndex = (int)e.Argument;

            //begin UI update
            StartWaitingBar(rowIndex);
            //get provider id cell
            GridViewDataRowInfo row = _proivderGridView.Rows[rowIndex];
            GridViewCellInfo provIdCell = row.Cells[ "ProviderId" ];

            var providerData = GetProviderData(Convert.ToInt32( provIdCell.Value));
            var provider =  ProviderFactory.CreateProvider(providerData);
            provider.Synchronize();
            e.Result = rowIndex;

        }
        catch (Exception exception)
        {
           return;
        }
    }

And code for worker creation:

           BackgroundWorker worker = new BackgroundWorker();
           worker.DoWork += worker_DoWork;
           worker.RunWorkerCompleted += worker_RunWorkerCompleted;
           worker.WorkerSupportsCancellation = true;
           worker.RunWorkerAsync(args.RowIndex);
          _syncWorkers.Add(providerId,worker);
           ...
            var worker = _syncWorkers[providerId];

            if(worker.IsBusy)
             {
                 worker.CancelAsync();
             }
            else
            {
                worker.RunWorkerAsync(args.RowIndex);
            }   

Solution provided here seems not working for me beacuse it works for recurring operations (for which background worker is created, I suppose). Do I have to use threads(abort and join) for my purposes because I should provide possibilities for user to cancel long-running operation?

Need your advice.

Thanks in advance.

+1  A: 

You've got to check e.Cancel within your DoWork method, which is missing from your code-snippet, but you've got to change your download method to an async call too, you're calling the method and wait for the answer within the dowork. Which is possible, but it won't check for the cancel flag in mean time.

Check the solution you posted (line 3):

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

    _resetEvent.Set(); // signal that worker is done 
} 
riffnl
Thank you for reply! I understand your point, but do I have to use backgraound worker at all? I can use async operation (as I did it initially) and use AutoResetEvent to achieve cancelling...
Sharov
As riffnl stated, `BackgroundWorker` does support cancellation. You don't have to use `BackgroundWorker`, but I recommend moving *up* in abstraction to `Task`, rather than *down* in abstraction to asynchronous delegates.
Stephen Cleary
@rifnl, it's not e.Cancel.
Henk Holterman
@Stephen, does .net 3.5 support Tasks?
Sharov
@Sharov: Bgw, Task and Async all have the same imitations. None of those can interrupt `Synchronize()` safely.
Henk Holterman
@Sharov: not officially. Unofficial support exists as part of the [Rx library](http://msdn.microsoft.com/en-us/devlabs/ee794896.aspx).
Stephen Cleary
@Sharov: what Henk means is that any cancellation scheme should be *cooperative*. So, `Synchronize` needs to check for cancellation somehow (periodic polling or listening to an event) and voluntarily exit when cancellation is requested.
Stephen Cleary
+2  A: 

You cannot use Backgroundworker.CancelAsync() to cancel a long running I/O action. Like rifnl answered, the DoWork has to check worker.CancellationPending and set e.Cancel.

But you shouldn't use Thread.Abort() either. It could destabilize your process.

The solution you need has to come from provider.Synchronize(); somehow.

PS: and catch { return; } is horrible. Remove the entire try/catch and let the Bgw handle exceptions.

Henk Holterman
Alternatively, if its possible to give the Synchronize a TimeOut value, you could put the synchronize in a while(!e.CancellationPending) privider.Synchronize(TimeOut); //if possibleloop, while continuing the function you currently have.
greggorob64