C# 通过BackgroundWorker组件从web下载许多页面

C# 通过BackgroundWorker组件从web下载许多页面,c#,asynchronous,download,backgroundworker,C#,Asynchronous,Download,Backgroundworker,我有许多网址(约800)从网上下载。我有一个类:HttpDownloader.cs,它与HttpWebRequest类一起使用来下载和获取html页面。在那之后,我用正则表达式对页面进行了修改 我想使用BackgroundWorker组件,但我不知道如何为所有的页面使用它。通过循环或类似的方式 我的代码: 我尝试使用ThreadPool,但它确实存在问题。我尝试了4个url,但没有成功 foreach (string link in MyListOfUrls) { Th

我有许多网址(约800)从网上下载。我有一个类:HttpDownloader.cs,它与HttpWebRequest类一起使用来下载和获取html页面。在那之后,我用正则表达式对页面进行了修改

我想使用BackgroundWorker组件,但我不知道如何为所有的页面使用它。通过循环或类似的方式

我的代码:

我尝试使用ThreadPool,但它确实存在问题。我尝试了4个url,但没有成功

      foreach (string link in MyListOfUrls)
      {
 ThreadPool.QueueUserWorkItem((o) => {

           HttpDownloader httpDownload = new HttpDownloader(link);
           string htmlDoc = httpDownload.GetPage();//get the html of the page 
           HtmlDocument doc=doc.LoadHtml(htmlDoc);//load html string to doc for pharsing
           DoPharsing();//my func for pharsing
           Save();//save into dataBase
  });
      }
因为我在func中与数据库和DataTable的连接一起使用,所以使用ThreadPool时会出现异常:

“功能评估已禁用,因为以前的功能评估 超时。必须继续执行才能重新启用函数 评估。”

因此,我无法从DataTable中获取数据。也许我需要下载所有内容,然后再进行修改和保存

如何通过BackgroundWorker组件将其更改为Async

p、 不要建议我使用异步Tpc,因为我没有下载它


谢谢

这取决于您想要分离的内容,整个循环,或者只是循环的下载部分。显然,如果希望整个循环都在后台,那么最简单的方法就是使用线程池

注意,您可能需要更改解析和保存函数,以便将HTML文档传递给每个函数

ThreadPool.QueueUserWorkItem((o) => {
  foreach (string link in MyListOfUrls)
  {
    HttpDownloader httpDownload = new HttpDownloader(link);
    string htmlDoc = httpDownload.GetPage();//get the html of the page
    HtmlDocument doc=doc.LoadHtml(htmlDoc);//load html string to doc for pharsing
    var result = DoPharsing(doc);//my func for pharsing
    Save(result);//save into dataBase
 } 
});

要同时下载多个链接,只需切换到创建线程的位置即可:

foreach (string link in MyListOfUrls)
{
  ThreadPool.QueueUserWorkItem((o) => {
    HttpDownloader httpDownload = new HttpDownloader(link);
    string htmlDoc = httpDownload.GetPage();//get the html of the page
    HtmlDocument doc=doc.LoadHtml(htmlDoc);//load html string to doc for pharsing
    var result = DoPharsing(doc);//my func for pharsing
    Save(result);//save into dataBase
  });
 } 

(我认为用户在这里创建线程池比创建数百个后台工作人员要好)。

我最终找到了答案
这是我的密码:

static BackgroundWorker[] d=new BackgroundWorker[MyListOfUrls.Length];
  string html=new string[MyListOfUrls.Length]

  static void Main(string[] args)
  {
    for (int i = 0; i < MyListOfUrls.Length; i++)
    {
         d[i]=new BackgroundWorker{WorkerReportsProgress=true};
         d[i].DoWork += new DoWorkEventHandler(worker2_DoWork);
         d[i].ProgressChanged += new ProgressChangedEventHandler(Program_ProgressChanged);
         d[i].RunWorkerAsync(i);
         d[i].RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
         Thread.Sleep(1000);
    }
  }  

  static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
      Console.WriteLine("End");
  }

  static void Program_ProgressChanged(object sender, ProgressChangedEventArgs e)
  {
      Console.WriteLine(e.ProgressPercentage.ToString());
  }

  static void worker2_DoWork(object sender, DoWorkEventArgs e)
  {
      var worker = (BackgroundWorker)sender;
      worker.ReportProgress((int)e.Argument);

      HttpDownloader httpDownload = new HttpDownloader(link);
      html[(int)e.Argument] = httpDownload.GetPage();

      Thread.Sleep(500);
  }
static BackgroundWorker[]d=新的BackgroundWorker[MyListOfUrls.Length];
字符串html=新字符串[MyListOfUrls.Length]
静态void Main(字符串[]参数)
{
for(int i=0;i
如果有人知道如何做得更好,我会很高兴。 萨克斯,
Chani

您希望同时执行多个下载,还是将下载与GUI分离(使其异步)?(顺便说一句,这是解析,不是篡改)@digEmAll,我想同时执行多个下载。更快地下载所有页面。您尝试过什么?互联网上有很多关于背景工作者的教程。你在这些教程中取得了多大的进步?你具体在做什么?请发布您尝试使用BackgroundWorker的代码。这是我不久前为一个关于如何使用BackgroundWorker的问题编写的教程。@Merlyn Morgan Graham,我尝试了没有背景工人类的线程。但是它根本不起作用。你也可以做
Parallel.ForEach
。这里实现的问题是它不支持干净的取消。您必须等到线程完成后才能完全终止您的下载—无论是在下载完成时,还是在连接超时时。唯一的解决方案是使用非阻塞的下载机制,在这种情况下,您不需要自己排队线程。与上面的2个示例有什么不同??是异步下载吗,就像同时下载页面一样???@Chanipoz:这里有三个例子。前两个几乎相同,并且只会帮助您避免GUI挂起。他们不会同时下载多个文档。第三个可以同时下载多个文档,但可以将大量线程池工作项排队。我猜,因为它“让他们排队”,所以这不是问题,而且它在内部只会同时运行这么多任务。前两个示例之间唯一的区别是,在第二个示例中,您获得了完成的事件,让UI知道您已经完成了。我对您的线程池解决方案不太满意。。。我们必须假设
GetPage
是同步运行的,因此会阻塞相当长的时间。在这种情况下,它不是ThreadPool的合适工作负载。线程池将饿死,因为它的设计目的是将线程数保持在最小值,因此,为响应大型工作队列而旋转新线程会出现相当大的延迟。反过来,这将影响其他不相关的API,例如Threading.Timer,它在ThreadPool中执行回调。现在我们的计时器工作不正常!。
static BackgroundWorker[] d=new BackgroundWorker[MyListOfUrls.Length];
  string html=new string[MyListOfUrls.Length]

  static void Main(string[] args)
  {
    for (int i = 0; i < MyListOfUrls.Length; i++)
    {
         d[i]=new BackgroundWorker{WorkerReportsProgress=true};
         d[i].DoWork += new DoWorkEventHandler(worker2_DoWork);
         d[i].ProgressChanged += new ProgressChangedEventHandler(Program_ProgressChanged);
         d[i].RunWorkerAsync(i);
         d[i].RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
         Thread.Sleep(1000);
    }
  }  

  static void RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  {
      Console.WriteLine("End");
  }

  static void Program_ProgressChanged(object sender, ProgressChangedEventArgs e)
  {
      Console.WriteLine(e.ProgressPercentage.ToString());
  }

  static void worker2_DoWork(object sender, DoWorkEventArgs e)
  {
      var worker = (BackgroundWorker)sender;
      worker.ReportProgress((int)e.Argument);

      HttpDownloader httpDownload = new HttpDownloader(link);
      html[(int)e.Argument] = httpDownload.GetPage();

      Thread.Sleep(500);
  }