0

I need to download a few text from different urls, then I am using the CountDownEvent to handle the number of times that my event Donwnload is completed, but the thing is my CountDownEvent never is set to Zero and this remains waiting.

Any idea what is wrong with this code?

namespace WebApplication.AsyncCall
{
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Threading;

    public partial class _Default : System.Web.UI.Page
    {
        private CountdownEvent countDown = null;
        public CountdownEvent CountDown
        {
            get
            {
                if (this.countDown == null)
                {
                    this.countDown = new CountdownEvent(1);
                }

                return this.countDown;
            }
        }

        private List<string> text = null;
        public List<string> Text
        {
            get
            {
                if (this.text == null)
                {
                    this.text = new List<string>();
                }

                return this.text;
            }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            List<string> rssSources = new List<string>();

            rssSources.Add(@"http://news.yahoo.com/rss/entertainment");
            rssSources.Add(@"http://go.microsoft.com/fwlink/?linkid=84795&clcid=409");

            foreach (string uri in rssSources)
            {
                this.CountDown.AddCount();
                LoadSources(uri);
            }

            this.CountDown.Signal();
            this.CountDown.Wait();
        }

        private void LoadSources(string uri)
        {
            WebClient client = new WebClient();
            client.DownloadStringAsync(new Uri(uri, UriKind.Absolute));

            client.DownloadStringCompleted += (s, a) =>
            {
                if (a.Error == null && !a.Cancelled)
                {
                    this.Text.Add(a.Result);
                    this.CountDown.Signal();
                }
            };
        }
    }
}
luis_laurent
  • 784
  • 1
  • 12
  • 32
  • Is the async call being completed before the listener is being added, by any chance? Try moving `client.DownloadStringAsync(new Uri(uri, UriKind.Absolute));` to the end of the `LoadSources` method, and see if that works. – Jeff Nov 28 '12 at 01:29
  • 1
    Is the callback definitely being called? EDIT: Also, you could try polling the value of `CountDown` in a while loop instead of using `this.CountDown.Wait();` in order to aid with debugging. – Jeff Nov 28 '12 at 02:52
  • 2
    If any error occurs in your download, then the `CountDown` won't be signaled. Also, it's possible that two callbacks will execute at the same time on different threads, and the call to `this.Text.Add` will be called simultaneously by two different threads. That's likely to cause some serious problems. – Jim Mischel Nov 28 '12 at 03:02
  • thanks for your advice, I will add a lock statement to avoid that. – luis_laurent Nov 28 '12 at 18:59

1 Answers1

0

I finally figure out how to solve my problem, the thing is despite that I am firing my download event asynchronous it seems they still are executed on the main thread, which mean that this.CountDown.Wait() is being invoked before that any download is completed then my this.CountDown is not being signaled therefore this.CountDown never is set to zero and this remains waiting.

Here what I did:

Into the foreach I replaced the call to the method LoadSources(uri) by a ThreadPool.QueueUserWorkItem which queues a method for execution. The method executes when a thread pool thread becomes available.

ThreadPool.QueueUserWorkItem(new WaitCallback(LoadSources), (object)uri);

I also had to change the LoadSources method to fit my adjustments.

private void LoadSources(object uri)
{
    WebClient client = new WebClient();
    client.DownloadStringAsync(new Uri(uri.ToString(), UriKind.Absolute));

    client.DownloadStringCompleted += (s, a) =>
    {
        lock (thisLock)
        {
            try
            {
                if (a.Error == null && !a.Cancelled)
                {
                    this.Text.Add(a.Result);
                }
            }
            finally
            {
                this.CountDown.Signal();
            } 
        }
    };
}

And as you can see I added a lock statement to avoid that two or more threads try to call this.Text.Add simultaneously

Before that I just declared a private object to lock on.

private Object thisLock = new Object();
luis_laurent
  • 784
  • 1
  • 12
  • 32