使方法运行异步的最佳方式是什么?
问题描述:
嗨,我是新的异步编程。我如何运行我的方法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);
}
}
答
- 你不会想在同一时间下载2500页,因为这将是您的客户端和服务器的问题。相反,我添加了一个并发下载限制(默认为10)。网页将一次下载10页。 (或者如果你正在运行一台超级计算机,你可以将其改为2500))
- 通用列表(我认为它是你的情况下的一个字符串列表)默认情况下不是线程安全的,因此你应该同步对Add方法。我还补充说。
下面是更新的源代码与并发呼叫
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);
}
}
}
}
答
这是最好从内部工作,继续出代码转换为async
的配置量asynhcronously下载页面。同时使用Task.WhenAll
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;
}
}
那么你可以开始任意数量的这些:按照沿途的最佳做法,如避免async void
,使用Async
后缀,并返回结果,而不是修改共享变量
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();
}
虽然你已经将它标记为“async”,但是你的两种方法都是同步的。 –
好的我已经编辑并从方法中删除了异步searchForLinks – Icet
方法应该返回'async void'的唯一时间是GUI事件处理程序。 https://msdn.microsoft.com/en-us/magazine/jj991977.aspx – MickyD