Подтвердить что ты не робот

С# зависает и застревает после Application.Run() для цикла for

Я создаю программу, которая просматривает несколько веб-сайтов и что-то делает.

После серфинга, как 5 URL-адресов успешно, программа зависает после строки Application.Run(). Программа даже не входит в функцию обработчика и просто застревает. в настоящее время использование ЦП составляет 0.

Я попытался закрыть потоки любым возможным способом. Что я делаю неправильно?

Я делаю это так:

[STAThread]
private static void Main(string[] args) 
{
    for (int i = 0; i < urls.Count; i++) 
    {
        var th = new Thread(() = > 
        {
            var weBrowser = new WebBrowser();
            weBrowser.AllowNavigation = true;
            weBrowser.DocumentCompleted += Handler;
            weBrowser.Navigate(urls[i]);
            Application.Run();
        });
        th.SetApartmentState(ApartmentState.STA);
        th.Start();
        th.Join();
    }
}

И моя функция Handle:

private static void Handler(object sender, WebBrowserDocumentCompletedEventArgs e) 
{
    WebBrowser weBrowser = sender as WebBrowser;
    var htmlDocument = weBrowser.Document;

    /*do something*/

    Application.Exit();
    Application.ExitThread();

    weBrowser.Dispose();
    weBrowser.Stop();

    Thread.CurrentThread.Abort();
}

Моя проблема очень похожа на эту: Application.Run() приводит к зависанию приложения

В этом вопросе также нет ответа.

Спасибо!

4b9b3361

Ответ 1

Я думаю, что вы делаете несколько ошибок:

  • вы присоединяетесь к внешнему виду
  • вы вызываете Application.Exit() в каждом вызове обработчика

Вы должны переместить соединение вне цикла for и не вызывать Application.Exit.

Следующий образец, кажется, работает хорошо:

static class Program
{
  [STAThread]
  static void Main()
  {
     var urls = new List<string>() { 
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com",
        "http://stackoverflow.com"};

     var threads = new Thread[urls.Count];

     for (int i = 0; i < urls.Count; i++)
     {
        threads[i] = new Thread((url) =>
        {
           var weBrowser = new WebBrowser();
           weBrowser.AllowNavigation = true;
           weBrowser.DocumentCompleted += Handler;
           weBrowser.Navigate(url as string);
           Application.Run();
        });
        threads[i].SetApartmentState(ApartmentState.STA);
        threads[i].Start(urls[i]);
     }

     foreach (var t in threads)
        t.Join();

     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);
     Application.Run(new Form1());
  }

  private static void Handler(object sender, WebBrowserDocumentCompletedEventArgs e)
  {
     WebBrowser weBrowser = sender as WebBrowser;

     var htmlDocument = weBrowser.Document;

     /*do something*/

     Application.ExitThread();

     weBrowser.Dispose();
     weBrowser.Stop();
  }
}

Ответ 2

Использование urls[i] в ваших исходных фрагментах неверно. Поиск документации С# для закрытий. Вам нужно будет сделать локальную копию перед ее использованием.

Кроме того, вы должны поменять weBrowser.Dispose() и weBrowser.Stop(). Вы больше не можете останавливать удаленный браузер (если требуется Stop).

Наконец, не прерывайте поток - он сам закончит.

Ответ 3

Я создал следующую программу, которая не очень отличается от версии @Marius Bancila, и я ее запускал много раз, она всегда успешно завершалась, вы можете проверить, слишком ли это зависает для вас, хотя, пожалуйста, помните из-за создания много потоков по умолчанию (30) в этом случае, будет большой спор. Кроме того, если операция обработчика длительная, то она, безусловно, задерживает завершение.

Для многопоточности создание всех потоков за один раз не является хорошей практикой, в идеале потоки обрабатываются по одному на ядро. Возможно, вы захотите рассмотреть использование Threadpool, где потоки могут быть назначены на основе количества процессоров, проверьте это Ссылка, на самом деле это еще проще при использовании библиотеки параллельных задач.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;

namespace Test
{
    class Program
    {
        [STAThread]
        private static void Main(string[] args) 
        {
            List<string> urls = new List<string>()
            {
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/","www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/","www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/", "www.http://google.com/", "www.http://google.com/",
                "www.http://google.com/"
            };

            List<Thread> threads = new List<Thread>();

            foreach (var url in urls)
            {
                var t = new Thread(() =>
                {
                    var weBrowser = new WebBrowser();
                    weBrowser.AllowNavigation = true;
                    weBrowser.DocumentCompleted += Handler;
                    weBrowser.Navigate(url);
                    Application.Run();
                });

                t.SetApartmentState(ApartmentState.STA);
                t.Start();
                threads.Add(t);
            }

            foreach (var t in threads)
                t.Join();

            Console.WriteLine("Application Finished");
        }

        private static void Handler(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            WebBrowser weBrowser = sender as WebBrowser;
            var htmlDocument = weBrowser.Document;
            weBrowser.Stop();
            weBrowser.Dispose();
            Application.ExitThread();
        }
    }
}

Ответ 4

Возможно, вы используете максимальное количество одновременных подключений для WebBrowser. Явным образом устанавливая это на большее число, вы можете одновременно считывать дополнительные потоки через браузер.

// Example Usage:
ServicePointManager.DefaultConnectionLimit = 10;

Имейте в виду, что есть увеличение производительности, увеличивая это число выше значения по умолчанию (я думаю, это 4), поскольку у вас будет гораздо больше сетевого трафика, который необходимо обработать.

Для получения дополнительной информации см. статью MSDN для ConnectionLimit.

Ответ 5

Я не понимаю, что вы хотели бы достичь с помощью Application.Run внутри цикла.

Почему вы используете компонент WebBrowser? Если вы просто разбираете веб-страницу, лучше использовать

string urlAddress = "http://stackoverflow.com"; 
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(urlAddress);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
    StreamReader reader= null;
    if (response.CharacterSet == null)
        reader = new StreamReader(response.GetResponseStream());
    else
        reader = new StreamReader(response.GetResponseStream(), Encoding.GetEncoding(response.CharacterSet));
    string data = reader.ReadToEnd();
    response.Close();
    reader.Close();
}

или

using (WebClient client = new WebClient())
{
    string html = client.DownloadString("http://stackoverflow.com");
}

Для анализа html посмотрите Html Agility Pack или что-то подобное.

Если это консольное приложение, вам не нужно вызывать Application.Run(), в противном случае вам следует подумать о том, чтобы показать заставку с прогрессом, чтобы дать пользователю некоторую обратную связь.