多线程

时间:2010-02-08 15:50:31

标签: c# multithreading design-patterns

我在这里发布了很多关于多线程的内容,而伟大的stackoverflow社区帮助我理解了多线程。

我在网上看到的所有例子都只涉及一个帖子。

我的申请是一家保险公司(家族公司......全部免费)的刮刀。无论如何,用户可以选择他们想要运行的线程数。例如,假设用户希望应用程序一次抓取5个站点,然后在当天晚些时候他选择20个线程,因为他的计算机没有做任何其他事情,因此它有足够的资源。

基本上,应用程序会建立一个包含1000个网站的列表。线程关闭并执行此操作并更新UI并构建列表。

当那个完成后,调用另一个线程来开始抓取。根据用户设置使用的线程数,它将创建x个线程。

创建这些线程的最佳方法是什么?我应该在列表中创建1000个线程。并循环通过他们?如果用户设置了5个线程来运行,它将一次循环5个。

我理解线程,但它是应用程序逻辑,它正在抓住我。

网上有什么想法或资源可以帮助我吗?

9 个答案:

答案 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)

如果您真的只是想要应用程序,请使用别人已经花时间开发和完善的东西:

http://arachnode.net/

  

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)

基本逻辑是:

您有一个队列,您可以在其中放置U​​RL以进行抓取,然后创建线程并使用每个线程都可以访问的队列对象。让线程开始循环:

  1. 锁定队列
  2. 检查队列中是否有项目,如果没有,则解锁队列和结束线程
  3. 将队列中的第一项出列
  4. 解锁队列
  5. 处理项目
  6. 调用更新UI的事件(记住锁定UI控制器)
  7. 返回第1步
  8. 让线程执行“从队列中获取东西”部分(拉动作业),而不是给他们提供网址(推动工作),这样你只需说出

    YourThreadManager.StartThreads(numberOfThreadsT​​heUserWants);

    其他一切都是自动发生的。请参阅其他回复以了解如何创建和管理线程。

答案 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文章。

基本上,您需要创建(并开始)适当的线程数,在您的情况下,该数字来自用户。这些线程中的每一个都应该处理一个站点,然后找到需要处理的下一个站点。即使你不使用对象本身(虽然听起来它很适合你的目的,虽然我显然有偏见!)它应该给你一些很好的洞察如何这种事情会完成。