作者:mobiledu2502859163 | 来源:互联网 | 2023-10-10 13:06
并行请求刮取网站的多个页面我想用一个包含大量有趣数据页面的网站,但由于源非常大,我想multithreading并限制过载。我使用Parallel.ForEach来启动10个任务的
并行请求刮取网站的多个页面
我想用一个包含大量有趣数据页面的网站,但由于源非常大,我想multithreading并限制过载。 我使用Parallel.ForEach
来启动10个任务的每个块,然后在main for
循环中等待,直到活动线程的数量开始下降到阈值以下。 为此我使用活动线程的计数器,我在使用WebClient
启动新线程时递增,并在触发WebClient
的DownloadStringCompleted
事件时递减。
最初的问题是如何使用DownloadStringTaskAsync
而不是DownloadString
并等待Parallel.ForEach
启动的每个线程都已完成。 这已通过一种解决方法解决:主要foor循环中的计数器( 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 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(jsonData); RecordData(root) } }
如果您想要一个优雅的解决方案,您应该使用Microsoft的Reactive Framework。 这很简单:
var source = db.ListOfUrls; // Thousands urls var query = from uri in source.ToObservable() from jsonData in Observable.Using( () => new WebClient(), wc => Observable.FromAsync(() => wc.DownloadStringTaskAsync(uri))) select new { uri, json = JsonConvert.DeserializeObject(jsonData) }; IDisposable subscription = query.Subscribe(x => { /* Do something with x.uri && x.json */ });
这就是整个代码。 这是很好的multithreading,它一直在控制之下。
只需NuGet“System.Reactive”即可得到这些位。
Parallel.ForEach
将创建ProcessorCount任务以执行源Enumerable中每个项目的function。 它将注意没有很多任务,并将等待执行所有项目和任务。
Task.WhenAll
只等待给定的任务,它不执行它们。 在你的手上以正确的方式执行它们而不是一次执行它们。
但是你的代码有一些错误。 函数RecordUri
将返回一个必须等待的任务,否则ForEach将创建越来越多的函数,因为函数永远不会知道当前任务何时完成。 同样有问题的是,您在任务中创建任务,第一个任务不执行任何操作,然后等待第一个任务。
您可能还想看看Parallel.ForEach
这个重载https://msdn.microsoft.com/en-us/library/dd782934(v=vs.110).aspx
编辑
使用等待DownloadStringTaskAsync而不是DownloadString应该通过在等待DownloadString数据到达时释放线程来提高速度吗?
否。当任务正在等待外部资源时,它进入Suspended状态(Windows api没有使用某些旧的/脏迭代等待)。 所以没有太大区别。 不同之处在于编译异步代码时编译器将产生的开销。 DownloadStringTaskAsync
将创建包含长操作的任务。 如果您使用等待它,您将自己附加到该任务(通过ContinueWith)。 所以你只需创建一个等待另一个的任务。 这是我在上部文本中讨论的开销。
我的方法是:在Parallel.ForEach中使用同步方法 。 线程将由PLinq完成,您可以自由继续。
记住“亲吻”
上述就是C#学习教程:并行请求刮取网站的多个页面分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—编程笔记