Kevin Kohut
Kevin Kohut

Reputation: 53

Waiting for all async WebClient calls to finish

I am using the WebClient class in C#, 4.0. I need to hit a REST service with 30,000 different IDs, and grab the status result (200 or 404). Here is the method that makes the calls (eventCounter is a CountdownEvent object):

private void doWork()
    {
        initDB();
        List<string> _lines = new List<string>();
        //pull all UpcIds into a List
        using (StreamReader _rdr = new StreamReader(@"C:\Users\kkohut\Dropbox\ROVI\Application Support\BestBuy\upc_test2.txt"))
        {
            string _line;
            while ((_line = _rdr.ReadLine()) != null)
            {
                _lines.Add(_line);
            }
        }

        numIds = _lines.Count();

        for (int i = 0; i < numIds; i++)
        {
            string _upcId = _lines[i];
            WebClient c = new WebClient();
            c.DownloadDataCompleted += new DownloadDataCompletedEventHandler(c_DownloadDataCompleted);
            c.DownloadDataAsync(new Uri(BASE_URL + _upcId), _upcId);
        }
        //this is not working correctly. Code execution hits this line and waits, without processing any of the 
        //the DownloadDataCompleted eventhandlers
        eventCounter.Wait();
    }

Here is the DownloadDataCompleted event handler

void c_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
    {
        DataSet _ds = new DataSet();
        string _upcId = e.UserState.ToString();
        string _status = "404";
        try
        {
            if (!e.Cancelled && e.Error == null)
            {
                string _result = System.Text.Encoding.UTF8.GetString(e.Result);
                if (_result.IndexOf("<code>200</code>") > 0)
                {
                    _status = "200";
                }
            }
        }
        catch (Exception ex)
        {
            _status = "404";
        }
        finally
        {
            updateDB(_upcId, _status);
            eventCounter.Signal(1);
            txtLog.Text += string.Format("{0}\t{1}\t{2}\r\n",ctr, _upcId, _status);
        }
    }

If I comment out the eventCounter.Wait() statement, the calls work, but I have no way of knowing when they complete. This is a winforms app, so as long as I keep the form running, all the calls complete. But if I uncomment the eventCounter.Wait() statement, no calls get processed. It appears that the Wait() statement is blocking the async calls to start with. Every example I have found uses this approach, but none of them are signaling the CountdownEvent in the completed event handler. Thoughts?

Upvotes: 0

Views: 3421

Answers (1)

dtb
dtb

Reputation: 217401

The WebClient Class implements the Event-based Asynchronous Pattern (EAP).

In this pattern, the XXXAsync Method captures the current SynchronizationContext (i.e. the UI thread in a WPF or WinForms application). When the operation completes, the event handler is executed in this context.

(See also: On which thread(s) does WebClient raise its events?)

Problem: If you invoke a blocking method on the UI thread, the event handler will not run before the blocking method returns.

Solution: Wait asynchronously for the CountdownEvent to complete, not synchronously.

You can use the ThreadPool.RegisterWaitForSingleObject Method to register a callback for the WaitHandle of the CountdownEvent.

Upvotes: 2

Related Questions