2
\$\begingroup\$

I had previously asked this question on SO. The answer pointed me towards using the task parallel library for multiple parallel downloads. I'm not actually utilizing this exact code, but it got me to rethink my design.

The one open issue that wasn't really addressed (and I didn't ask it), cancelling the a local WebClient. It isn't as simple as just calling WebClient.CancelASync(); since the scope of WebClient is long gone by the time you need to cancel.

This isn't my code or even how I am approaching the problem, but just part of the test example I put together to see how this works. It seems to work, although it means having to wait for an event callback before the cancel is called. So I was wondering if there was another alternative.

private bool pendingCancel = false;
private Queue<Uri> queue = LoadQueue();

public void ASyncDownload()
{
    if (queue.Count == 0) return;

    var uri = queue.Dequeue();

    WebClient client = new WebClient();
    client.DownloadProgressChanged
        += (sender, e) =>
        {
            if (pendingCancellation)
            {
                ((WebClient)sender).CancelAsync();
                return;
            }

            //do something to report progress
        };

    client.DownloadDataCompleted
        += (sender, e) =>
        {
            if (!e.Cancelled || pendingCancel)
            {
                if (e.Error == null)
                {
                    // do something with e.Results
                }
                else
                {
                    // report error
                }
            }
            else
            {
                // report cancellation
            }
        };

    client.DownloadDataAsync(uri);
}

My thought process is for any long running downloads, waiting until the next item in the queue to exit would not be appropriate, so the idea would be to call CancelASync() on the sender of the DownloadProgressChanged event handler.

Is this the best alternative short of putting a webClient field in the class? And are there dangers or possible unpredictable behavior that I have not found in my testing?

\$\endgroup\$
  • 1
    \$\begingroup\$ Why are you trying to avoid using a field? That's exactly what they're for: storing objects that are needed for more than just one method call. \$\endgroup\$ – svick Aug 17 '12 at 11:52
  • \$\begingroup\$ @svick In the end, I'm not trying to avoid anything, but I see similar patterns in other code and I am trying to understand if it is possible to cancel this type of async operation \$\endgroup\$ – psubsee2003 Aug 17 '12 at 13:15
1
\$\begingroup\$

I am not able to comment on your post due to a lack of reputation, but when I did something similar to this, I used a CancellationToken. It's been around for quite some time now (.NET 4.0), and is generally accepted in most .NET Async APIs. It should achieve what you're trying to do. The documentation for the concept is located here.

You don't have to put a WebClient in your class (as a field) if you don't want to. Most of the interactions I've seen with web servers (in C#) behave very similar to the Entity Framework in that you generally send a request, which yields some form of IEnumerable that you have to deal with. So, you could wire up a service util for handling the common bits for a web client, and generate a unique one per request. I did something similar to that, and it worked well, but you need to be cautious of shared state in that scenario (construction really needs to be construction - not use of a singleton - if you're generating a unique client every time).

I hope this helps!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.