我在这里发布了很多关于多线程的内容,而伟大的stackoverflow社区帮助我理解了多线程。
我在网上看到的所有例子都只涉及一个帖子。
我的申请是一家保险公司(家族公司......全部免费)的刮刀。无论如何,用户可以选择他们想要运行的线程数。例如,假设用户希望应用程序一次抓取5个站点,然后在当天晚些时候他选择20个线程,因为他的计算机没有做任何其他事情,因此它有足够的资源。
基本上,应用程序会建立一个包含1000个网站的列表。线程关闭并执行此操作并更新UI并构建列表。
当那个完成后,调用另一个线程来开始抓取。根据用户设置使用的线程数,它将创建x个线程。
创建这些线程的最佳方法是什么?我应该在列表中创建1000个线程。并循环通过他们?如果用户设置了5个线程来运行,它将一次循环5个。
我理解线程,但它是应用程序逻辑,它正在抓住我。
网上有什么想法或资源可以帮助我吗?
答案 0 :(得分:3)
您可以考虑使用线程池:
using System;
using System.Threading;
public class Example
{
public static void Main()
{
ThreadPool.SetMaxThreads(100, 10);
// Queue the task.
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
Console.WriteLine("Main thread does some work, then sleeps.");
Thread.Sleep(1000);
Console.WriteLine("Main thread exits.");
}
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo)
{
Console.WriteLine("Hello from the thread pool.");
}
}
答案 1 :(得分:2)
这个刮刀,在运行时会使用大量的CPU吗?
如果它与这1000个远程站点进行了大量通信,下载了他们的页面,这可能比实际分析页面花费的时间更长。
您的用户拥有多少CPU内核?如果他们有2(这些日子很常见),那么超过两个同时执行分析的线程,他们就不会看到任何加速。
所以你可能需要“并行化”页面的下载。我怀疑你需要为分析页面做同样的事情。
查看异步IO,而不是显式多线程。它允许您并行启动一堆下载,然后在每个下载完成后回调。
答案 2 :(得分:1)
我认为这个例子基本上就是你需要的。
public class WebScraper
{
private readonly int totalThreads;
private readonly List<System.Threading.Thread> threads;
private readonly List<Exception> exceptions;
private readonly object locker = new object();
private volatile bool stop;
public WebScraper(int totalThreads)
{
this.totalThreads = totalThreads;
threads = new List<System.Threading.Thread>(totalThreads);
exceptions = new List<Exception>();
for (int i = 0; i < totalThreads; i++)
{
var thread = new System.Threading.Thread(Execute);
thread.IsBackground = true;
threads.Add(thread);
}
}
public void Start()
{
foreach (var thread in threads)
{
thread.Start();
}
}
public void Stop()
{
stop = true;
foreach (var thread in threads)
{
if (thread.IsAlive)
{
thread.Join();
}
}
}
private void Execute()
{
try
{
while (!stop)
{
// Scrap away!
}
}
catch (Exception ex)
{
lock (locker)
{
// You could have a thread checking this collection and
// reporting it as you see fit.
exceptions.Add(ex);
}
}
}
}
答案 3 :(得分:1)
如果您真的只是想要应用程序,请使用别人已经花时间开发和完善的东西:
arachnode.net是一个完整而全面的.NET网络爬虫 下载,索引和存储 互联网内容包括电子邮件 地址,文件,超链接,图像, 和网页。
是否感兴趣或参与 屏幕抓取,数据挖掘,文本 采矿,研究或任何其他 应用哪里有高性能 爬行应用程序是关键 你的努力取得了成功, arachnode.net提供解决方案 你需要成功。
如果你也想自己写一个,因为写一个有趣的东西(我不久前写了一篇,是的,这很有趣)那么你可以参考arachnode.net提供的pdf,这真的解释了详细介绍了一个优秀的网络爬虫背后的理论:
http://arachnode.net/media/Default.aspx?Sort=Downloads&PageIndex=1
下载标题为“抓取网页”的pdf(顶部的第二个链接)。滚动到第2.6节标题:“2.6多线程爬虫”。这就是我用来构建我的爬虫的东西,我必须说,我认为它的效果非常好。
答案 4 :(得分:0)
基本逻辑是:
您有一个队列,您可以在其中放置URL以进行抓取,然后创建线程并使用每个线程都可以访问的队列对象。让线程开始循环:
让线程执行“从队列中获取东西”部分(拉动作业),而不是给他们提供网址(推动工作),这样你只需说出
YourThreadManager.StartThreads(numberOfThreadsTheUserWants);
其他一切都是自动发生的。请参阅其他回复以了解如何创建和管理线程。
答案 5 :(得分:0)
我通过创建一个工作类来解决类似的问题,该工作类使用回调来通知主应用程序工作者已完成。然后我创建一个包含1000个线程的队列,然后调用一个启动线程的方法,直到达到正在运行的线程限制,并使用由线程的ManagedThreadId键入的字典跟踪活动线程。当每个线程完成时,回调将从字典中删除其线程并调用线程启动器。
如果连接被删除或超时,则回调会将线程重新插入队列。锁定队列和字典。我使用线程池创建线程,因为与连接时间相比,创建线程的开销微不足道,并且它允许我在飞行中拥有更多线程。回调还提供了一个方便的位置来更新用户界面,甚至允许您在运行时更改线程限制。我一次有超过50个开放连接。请记住在app.config中增加MacConnections属性(默认为2)。
答案 6 :(得分:0)
考虑使用基于事件的异步模式(AsyncOperation和AsyncOperationManager类)
答案 7 :(得分:0)
我会使用队列和条件变量以及互斥锁,并且只启动请求的线程数,例如5或20(而不是1000)。
每个线程都在条件变量上阻塞。唤醒后,它会使第一个项目出列,解锁队列,使用该项目,锁定队列并检查更多项目。如果队列为空,则在条件变量上休眠。如果没有,请解锁,工作,重复。
当互斥锁被锁定时,它还可以检查用户是否已请求减少线程数。只需检查是否计数&gt; max_count,如果是,则线程自行终止。
每当有更多站点要排队时,只需锁定互斥锁并将其添加到队列中,然后在条件变量上进行广播。任何尚未运行的线程都会唤醒并开始新工作。
每当用户增加请求的线程数时,只需启动它们,它们将锁定队列,检查工作,并在条件变量上休眠或开始使用。
每个线程将不断从队列中拉出更多工作,或者正在休眠。您不需要超过5或20。
答案 8 :(得分:-1)
您可能需要查看有关CodeProject的ProcessQueue文章。
基本上,您需要创建(并开始)适当的线程数,在您的情况下,该数字来自用户。这些线程中的每一个都应该处理一个站点,然后找到需要处理的下一个站点。即使你不使用对象本身(虽然听起来它很适合你的目的,虽然我显然有偏见!)它应该给你一些很好的洞察如何这种事情会完成。