检测 webclient 的 DownloadComplete 事件处理程序执行是否完成?

Detect if webclient's DownloadComplete event handler execution finished?

我是整个 AsyncThreading 编程世界的新手。我被困在一个问题上。以下代码是为了更好地理解而简化的版本。

我正在努力做的三件事,

1) 使用 WebClient 循环点击 api,这是 Async 方法并开始下载数据
2) 下载 api 数据时使用这段时间处理其他数据并计算一些值
3) 确保所有下载完成,然后处理下载的数据并保存到文件和数据库

我能够完成 2 个步骤,但在第 3 步中,我不确定如何检测所有下载是否已完成,所以用 Google 搜索并找到 this 但问题是它需要 .net 4.5我正在研究 .net 4.0。所以基本上我需要解决方案来帮助我弄清楚如何检测所有 api 调用的下载是否已完成。

有一种方法可以使用循环调用计数来匹配已完成的数据项列表计数,但是如果只有一个或两个 api 调用出现错误,在这种情况下它将无限期等待。

下面是我的代码,

  class Program
  {
    public static List<StackRoot> AllQuestionRoot = new List<StackRoot>();
    static void Main(string[] args)
    {
        MyWebClient client = new MyWebClient();
        try
        {
            for (int i = 0; i < 10; i++)
            {
                client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(HandleQuestionDownloadCompleted);
                client.DownloadStringAsync(new Uri("http://api.stackexchange.com/2.2/questions?page=1&pagesize=20&order=desc&sort=creation&tagged=reporting-services&site=Whosebug"), waiter);
            }
        }
        catch (WebException exception)
        {
            string responseText;
            using (var reader = new StreamReader(exception.Response.GetResponseStream()))
            {
                responseText = reader.ReadToEnd();
            }
        }
        //Do some other stuff
        //calculate values 

        //How to make sure all my asynch DownloadStringCompleted calls are completed ?

        //process AllQuestionRoot data  depending on some values calculated above

        //save the AllQuestionRoot to database and directory

       Console.ReadKey();
    }

    private static void HandleQuestionDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null || !e.Cancelled)
        {
            StackRoot responseRoot = JsonConvert.DeserializeObject<StackRoot>(e.Result);

            AllQuestionRoot.Add(responseRoot);
        }
    }
}

如有困惑,请随时发表评论。如果还有其他方法可以实现我正在做的事情,请随意提及。无需按照我的方法,如果您有其他方法,请免费 comment.Any 指点的话,答案会很好。

作为旁注,您可以使用 Microsoft Async.NET 4.0

上使用 async-awit

所以,你需要有一些方法来等待一系列的结束 "tasks"。

既然您似乎知道自己有多少 "tasks",那么 CountdownEvent 就很合适:

  class Program
  {
    public static List<StackRoot> AllQuestionRoot = new List<StackRoot>();
    public static object criticalSection = new object();
    public static CountdownEvent countdown = new CountdownEvent(10);
    static void Main(string[] args)
    {
        MyWebClient client = new MyWebClient();
        try
        {
            for (int i = 0; i < 10; i++)
            {
                client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(HandleQuestionDownloadCompleted);
                client.DownloadStringAsync(new Uri("http://api.stackexchange.com/2.2/questions?page=1&pagesize=20&order=desc&sort=creation&tagged=reporting-services&site=Whosebug"), waiter);
            }
        }
        catch (WebException exception)
        {
            string responseText;
            using (var reader = new StreamReader(exception.Response.GetResponseStream()))
            {
                responseText = reader.ReadToEnd();
            }
        }
        //Do some other stuff
        //calculate values 

        //How to make sure all my asynch DownloadStringCompleted calls are completed ?

        //process AllQuestionRoot data  depending on some values calculated above

        //save the AllQuestionRoot to database and directory

        // Wait until all have been completed.
        countdown.Wait();

        Console.ReadKey();
    }

    private static void HandleQuestionDownloadCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null || !e.Cancelled)
        {
            StackRoot responseRoot = JsonConvert.DeserializeObject<StackRoot>(e.Result);

            // Adding to List<T> is not thread safe.
            lock (criticalSection)
            {
                AllQuestionRoot.Add(responseRoot);
            }

            // Signal completed. 
            countdown.Signal();
        }
    }
}