2016-02-13 145 views
1

我想写一个C#函数来使用http请求计算网页上的链接断开。由于我想快速完成这项工作,我为每个请求创建一个线程,然后简单地增加线程中的计数器。我的问题是,计数器在最后保持0,尽管我知道网站上有几个断开的链接。看起来像线程没有在主线程中设置变量。如何在C#中的其他线程在主线程中设置变量?

public volatile int found; 
public volatile int notfound; 

    public void GetBrokenLinks(WebBrowser website) 
    { 
    HtmlElementCollection links = website.Document.GetElementsByTagName("a"); 

     foreach (HtmlElement element in links) 
     { 
      string link = element.GetAttribute("href").ToString(); 
       Uri urlCheck = new Uri(link); 
       HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlCheck); 
       request.Timeout = 10000; 
       try 
       { 
        Thread link_checher_thread = new Thread(delegate() 
        { 
         HttpWebResponse response; 

         response = (HttpWebResponse)request.GetResponse(); 
         if (response.StatusCode == HttpStatusCode.OK) 
         { 
          response.Dispose(); 
          found++; 
         } 
         else if (response.StatusCode == HttpStatusCode.NotFound) 
         { 
          response.Dispose(); 
          notfound++; 
         } 
        }); 
        link_checher_thread.IsBackground = true; 
        link_checher_thread.Start(); 
       } 
       catch (Exception ex) 
       { 
        MessageBox.Show(ex.ToString()); 
       } 
     } 
     MessageBox.Show(found.ToString() + ", " + notfound.ToString()); 
    } 

我已经在互联网上搜索了几个小时,尝试了挥发性变量,但似乎没有任何工作。我怎么能强制线程设置主线程中的变量?

+1

为了“使这个快”,你不应该旋转每个请求线程,而是使用异步编程(只是谷歌'异步/ await')。 –

+4

在线程完成之前,您正在访问变量的方式太早。所以当然他们仍然是0.使用BackgroundWorker或任务来获得这个权利。 –

+0

Jancsik Zsolt,正如@HansPassant所说,你开始线程并从不检查它们是否完成。 – Eser

回答

1

有在你的代码两个主要问题:

  • 你是不是等待所有线程显示前完成结果。
  • ++不是线程安全的,您应该使用Interlocked.Increment以自动递增计数器。

您可以使用.Net框架中的任务轻松完成此任务。

public int found; 
public int notfound; 

public void GetBrokenLinks(WebBrowser website) 
{ 
    HtmlElementCollection links = website.Document.GetElementsByTagName("a"); 

    List<Task> tasks = new List<Task>(); 

    foreach (string element in links) 
     { 
      string link = element.GetAttribute("href").ToString(); 

      tasks.Add(Task.Factory.StartNew(() => 
      { 
       Uri urlCheck = new Uri(link); 
       HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlCheck); 
       request.Timeout = 10000; 
       HttpWebResponse response; 

       response = (HttpWebResponse)request.GetResponse(); 
       if (response.StatusCode == HttpStatusCode.OK) 
       { 
        response.Dispose(); 
        Interlocked.Increment(ref found); 
       } 
       else if (response.StatusCode == HttpStatusCode.NotFound) 
       { 
        response.Dispose(); 
        Interlocked.Increment(ref notfound); 
       } 
      } 
      )); 
     } 

     try 
     { 
      Task.WaitAll(tasks.ToArray()); 
     } 
     catch (AggregateException ae) 
     { 
      ae.Handle((e) => { MessageBox.Show(e.ToString()); return true; }); 
     } 


     MessageBox.Show(found.ToString() + ", " + notfound.ToString()); 
} 
+0

完美工作,谢谢指出问题!我不得不在任务内放置一个额外的try/catch,因为有些请求会返回超时异常。 –

+0

请注意不要尝试在线程中显示错误对话框。如果你想这样做,你可以使用this.Invoke在主(UI)线程上执行代码。 – Simon

2

计数器不会停留在0-问题更深入,更容易。

  • ++不是原子和
  • C#编译器和运行时能够优化我们的读访问了。

Bes事情:使用Interlocked类的方法来增加AND读取类。成品。他们使用原子API并为多线程操作。

2

正确的方法来增加.NET共享计数器是通过System.Threading.Interlocked.Increment(ref found)