.net 在C语言中一次下载多个HTML#

.net 在C语言中一次下载多个HTML#,.net,multithreading,.net,Multithreading,我有一个需要下载的网址列表(>10k),如果我使用单线程应用程序下载非常耗时,那么多线程或多backgroundworker实例中的哪一个是更好的选择,为什么 如果您有GUI,那么建议使用BackgroundWorker一般来说,多线程处理使用多个线程(可能在服务器CPU的多个内核上)在应用程序等待代码返回时并行处理代码。在3.5版中引入了一个优秀的.Net多线程工具,称为。多线程最大的性能改进之一是在循环内的重复进程中使用多线程 背景工人不同。它们可能是多线程的,也可能不是多线程的。这里的要点

我有一个需要下载的网址列表(>10k),如果我使用单线程应用程序下载非常耗时,那么多线程或多backgroundworker实例中的哪一个是更好的选择,为什么

如果您有GUI,那么建议使用
BackgroundWorker

一般来说,多线程处理使用多个线程(可能在服务器CPU的多个内核上)在应用程序等待代码返回时并行处理代码。在3.5版中引入了一个优秀的.Net多线程工具,称为。多线程最大的性能改进之一是在循环内的重复进程中使用多线程

背景工人不同。它们可能是多线程的,也可能不是多线程的。这里的要点是,应用程序代码不会暂停并等待后台进程完成。相反,主代码可以继续处理。当后台进程返回其结果时,会调用一个单独的方法。以下是来自以下方面的解释:

BackgroundWorker类允许您在服务器上运行操作 独立的、专用的线程。像下载这样耗时的操作 而数据库事务可能会导致用户界面(UI)看起来 就好像它在运行时停止了响应。当你 想要一个响应迅速的用户界面,您将面临与之相关的长时间延迟 通过这些操作,BackgroundWorker类提供了一个方便的 解决方案


多线程
多个backgroundworker实例
在技术上解决了相同的问题。基本上,您可以使用线程池来使用这些用法,或者更好地使用
Parallel.ForEach
。如果需要最大性能,请确保最小化线程和/或GUI之间的同步数据。

我的方法是:

  • 将链接放入队列中
  • 启动N个线程,其中N个是可配置的
  • 在每个线程中循环 取消链接队列 如果失败(队列为空),请退出线程 下载链接
  • 在主线程上做一些UI工作-GUI或控制台上每隔几秒钟编写一次类似“运行,下载x/y文件”的内容
  • 在主线程上,如果队列为空,请连接()其他线程

    • 我遇到了完全相同的问题,并实施了节流策略和C#4.0任务工厂来解决它。在这种设计中,所有下载都发生在一个单独的线程中,该线程由一个信号量的静态实例限制。信号量限制在app.config中设置

      public class Downloader
      {
          private static readonly Semaphore DownloadThrottle = 
                         new Semaphore(Properties.Settings.Default.ThrottleCount,     
                         Properties.Settings.Default.ThrottleCount);
          public Task<bool> DownloadTaskThread { get; set; }
          private string _url;
          private string _localFileName;
          public void Get(string url, string localFileName)
          {
              _url = url;
              _localFileName = localFileName;
              DownloadTaskThread = new Task<bool>(Worker);
              DownloadTaskThread.Start();
          }
          private bool Worker()
          {
              try
              {
                  DownloadThrottle.WaitOne(); 
                  using (WebClient wc = new WebClient())
                  {
                      wc.DownloadFile(_url, _localFileName);
                      return true;
                  }
              }
              catch (Exception ex)
              {
                  Console.WriteLine(ex.Message);
              }
              finally
              {
                  DownloadThrottle.Release();
              }
              return false;
          }
      }
      
      公共类下载程序
      {
      私有静态只读信号量DownloadThrottle=
      新信号量(Properties.Settings.Default.ThrottleCount,
      Properties.Settings.Default.ThrottleCount);
      公共任务下载任务线程{get;set;}
      私有字符串url;
      私有字符串\u localFileName;
      public void Get(字符串url、字符串localFileName)
      {
      _url=url;
      _localFileName=localFileName;
      DownloadTaskThread=新任务(工作者);
      下载TaskThread.Start();
      }
      私人布尔工人()
      {
      尝试
      {
      下载throttle.WaitOne();
      使用(WebClient wc=new WebClient())
      {
      wc.DownloadFile(_url,_localFileName);
      返回true;
      }
      }
      捕获(例外情况除外)
      {
      控制台写入线(例如消息);
      }
      最后
      {
      下载throttle.Release();
      }
      返回false;
      }
      }
      
      在这个类中,调用“Get”方法,它启动一个线程来执行工作。客户端类保留“DownloadTaskThread”实例的列表并轮询结果。当完成的任务数量达到可接受的水平时,会启动更多线程

      Worker类中的节流阀和main类中的节流阀需要“调优”,以便在完成工作和应用程序的整体响应性之间取得平衡

      使用信号量可以确保一次只执行一定数量的工作,从而保持UI的响应性


      使用该类,您可以访问操作是如何完成的,即成功与否。任务实例是强类型的,因此您可以使用适合您的应用程序的任何类型的结果。在我的示例中,它返回bool。与以前的C#版本相比,这是一个很大的优势,后者只提供ThreadPool.QueueUserWorkItem(没有返回值)。通用类是有文档记录的

      您应该使用的方法在很大程度上取决于您下载这10000页的速度和频率

      一般来说,单线程下载应用程序的平均速度约为每秒一页。您的结果将因您下载的站点而异。从yahoo.com上下载东西要比从有线调制解调器上托管的服务器上下载快得多。单线程下载应用程序的好处在于它非常容易编写。如果你只需要下载这些页面一次,那么就编写一个单线程应用程序,让它工作,然后吃一顿长长的午餐。您将在大约三小时内获得数据

      如果你有一个四核机器,你可以做大约四页每秒。只需编写单线程应用程序,将URL列表分成四个相等的部分,启动应用程序的四个实例,并定期吃午餐。当你回来时,你会得到数据的

      如果您将定期下载这些页面,那么您可以编写程序来维护URL的链接。旋转四个线程,每个线程基本上执行以下操作:

      while (queue not empty)
      {
          dequeue url
          download page
      }
      
      这将在与单线程下载程序的四个独立实例相同的时间内执行。事实上
      while (url queue is not empty)
      {
          client = dequeue WebClient // this will block if all clients are currently busy
          url = dequeue url
          client.DownloadDataAsync(url)
      }