【发布时间】:2017-09-18 17:10:25
【问题描述】:
我想抓取一个包含大量包含有趣数据的页面的网站,但由于源非常大,我想使用多线程并限制过载。
我使用Parallel.ForEach 来启动 10 个任务的每个块,并在主 for 循环中等待,直到启动的活动线程数降至阈值以下。为此,我使用了一个活动线程计数器,当使用WebClient 启动一个新线程时我会递增,并在WebClient 的DownloadStringCompleted 事件被触发时递减。
最初的问题是如何使用DownloadStringTaskAsync 而不是DownloadString 并等待在Parallel.ForEach 中启动的每个线程都已完成。这已通过解决方法解决:
主循环中的计数器(activeThreads)和Thread.Sleep。
使用await DownloadStringTaskAsync 而不是DownloadString 是否应该通过在等待DownloadString 数据到达时释放线程来提高速度?
回到最初的问题,有没有办法使用 TPL 更优雅地做到这一点,而无需涉及计数器的解决方法?
private static volatile int activeThreads = 0;
public static void RecordData()
{
var nbThreads = 10;
var source = db.ListOfUrls; // Thousands urls
var iterations = source.Length / groupSize;
for (int i = 0; i < iterations; i++)
{
var subList = source.Skip(groupSize* i).Take(groupSize);
Parallel.ForEach(subList, (item) => RecordUri(item));
//I want to wait here until process further data to avoid overload
while (activeThreads > 30) Thread.Sleep(100);
}
}
private static async Task RecordUri(Uri uri)
{
using (WebClient wc = new WebClient())
{
Interlocked.Increment(ref activeThreads);
wc.DownloadStringCompleted += (sender, e) => Interlocked.Decrement(ref iterationsCount);
var jsonData = "";
RootObject root;
jsonData = await wc.DownloadStringTaskAsync(uri);
var root = JsonConvert.DeserializeObject<RootObject>(jsonData);
RecordData(root)
}
}
【问题讨论】:
标签: c# multithreading async-await webclient parallel.foreach