什么是使方法运行异步的最佳方法?

时间:2015-11-02 09:21:14

标签: c# asynchronous

嗨,我是异步编程的新手。如何运行我的方法checkAvaible运行异步?如果可能,我想一次下载2500页,不要等到完成一次下载并启动另一次。我该怎么做?

private static void searchForLinks()
    {
        string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";


        for (int i = 0; i < 2500; i++)
        {
            string tmp = url;
            tmp += Convert.ToString(i);

            checkAvaible(tmp); // this method run async, do not wait while one page is downloading
        }

        Console.WriteLine(listOfUrls.Count());
        Console.ReadLine();
    }

    private static async void checkAvaible(string url)
    {
        using (WebClient client = new WebClient())
        {
            string htmlCode = client.DownloadString(url); 

            if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
                listOfUrls.Add(url);
        }
    }

2 个答案:

答案 0 :(得分:3)

  1. 您不希望同时下载2500页,因为这对您的客户端和服务器都是一个问题。相反,我添加了并发下载限制(默认为10)。网页将一次下载10页。 (或者,如果您正在运行超级计算机,则可以将其更改为2500)))
  2. 通用列表(我认为它是您的情况下的字符串列表)默认情况下不是线程安全的,因此您应该同步对Add方法的访问。我还补充说。
  3. 这是更新的源代码,可以通过可配置的并发呼叫数量下载页面

    private static List<string> listOfUrls = new List<string>();
    
    private static void searchForLinks()
    {
        string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";
    
        int numberOfConcurrentDownloads = 10;
    
        for (int i = 0; i < 2500; i += numberOfConcurrentDownloads)
        {
            List<Task> allDownloads = new List<Task>();
            for (int j = i; j < i + numberOfConcurrentDownloads; j++)
            {
                string tmp = url;
                tmp += Convert.ToString(i);
                allDownloads.Add(checkAvaible(tmp));
            }
            Task.WaitAll(allDownloads.ToArray());
        }
    
        Console.WriteLine(listOfUrls.Count());
        Console.ReadLine();
    }
    
    private static async Task checkAvaible(string url)
    {
        using (WebClient client = new WebClient())
        {
            string htmlCode = await client.DownloadStringTaskAsync(new Uri(url));
    
            if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
            {
                lock (listOfUrls)
                {
                    listOfUrls.Add(url);
                }
            }
        }
    }
    

答案 1 :(得分:1)

最好将代码转换为async,然后从内部进行处理。遵循最佳实践,例如避免async void,使用Async后缀,并返回结果而不是修改共享变量:

private static async Task<string> checkAvaibleAsync(string url)
{
  using (var client = new HttpClient())
  {
    string htmlCode = await client.GetStringAsync(url); 

    if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
      return url;
    else
      return null;
  }
}

然后,您可以使用Task.WhenAll

同时启动任意数量的这些内容
private static async Task<string[]> searchForLinksAsync()
{
  string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";

  var tasks = Enumerable.Range(0, 2500).Select(i => checkAvailableAsync(url + i));
  var results = await Task.WhenAll(tasks);
  var listOfUrls = results.Where(x => x != null).ToArray();

  Console.WriteLine(listOfUrls.Length);
  Console.ReadLine();
}