2015-06-26 5 views
0

У меня есть более 2000 URL-запросов, чтобы сделать, и с кодом ниже он занимает почти 2 минуты. Может ли кто-нибудь помочь мне ускорить процесс?Несколько webrequest через массив строк

private void button4_Click(object sender, EventArgs e) 
    { 
     WebRequest req; 
     WebResponse res; 
     string[] lines = File.ReadAllLines(@"c:\data\temp.txt"); 
     for (int i = 0; i < lines.Count(); i++) 
     { 
      req = WebRequest.Create(lines[i]); 
      res = req.GetResponse(); 
      StreamReader rd = new StreamReader(res.GetResponseStream(), Encoding.ASCII); 
      rd.Close(); 
      res.Close(); 
      textBox1.Text += "."; 
     } 
    } 

Большое спасибо

+0

Купить более быстрый интернет. – leppie

+0

Нить. Вы можете делать больше звонков одновременно. – Lloyd

ответ

0

Вы не можете ускорить вещи много потому, что узким местом является подключение к Интернету. Однако есть что-то, что вы можете сделать:

1) Не линии линий LINQ, это массив и его размер известен (микро оптимизация, вы никогда не заметите это изменение).

2) Используйте using для выпуска одноразовых объектов (ничего общего со скоростью, лучшей обработкой ошибок: если что-то пошло не так с вашим кодом, вы будете освобождать ресурсы с помощью GC).

3) Сделайте их параллельными. Это позволит ускорить вещи немного немного:

private void button4_Click(object sender, EventArgs e) 
{ 
    var lines = File.ReadAllLines(@"c:\data\temp.txt"); 

    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; 
    Parallel.ForEach(lines, options, line => 
    { 
     var request = WebRequest.Create(line); 

     using (var response = request.GetResponse()) 
     { 
      var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII); 

      // Do your stuff 

      BeginInvoke(new MethodInvoker(delegate 
      { 
       textBox1.Text += "."; 
      })); 
     } 
    }); 
} 

Несколько больше нот:

  • MaxDegreeOfParallelism устанавливает максимальное количество одновременных запросов. Несколько активных одновременных подключений не ускорят работу на неопределенный срок, и они могут даже замедлить работу. Некоторые испытания помогут вам установить это значение в разумным значением.

  • Нет ошибок при проверке, но сетевые вещи могут временно ошибаться, но после небольшой задержки они могут работать должным образом. Я предлагаю также читать System.Net.WebException: The remote name could not be resolved и this для операций ввода-вывода.

Чтобы сделать его более полный пример, ваш клик даже обработчик будет:

private void button4_Click(object sender, EventArgs e) 
{ 
    var options = new ParallelOptions { MaxDegreeOfParallelism = 4 }; 
    Parallel.ForEach(ReadUrlList(@"c:\data\temp.txt"), options, ProcessUrl); 
} 

Фактический код для обработки каждого URL и читать список URL:

private static string[] ReadUrlList(string path) 
{ 
    return File.ReadAllLines(@"c:\data\temp.txt"); 
} 

private void ProcessUrl(string url) 
{ 
    ProcessResponse(response => 
    { 
     using (var reader = new StreamReader(response.GetResponseStream(), Encoding.ASCII)) 
     { 
      // Do your stuff 

      // We're working on separate threads, to access UI we 
      // have to dispatch the call to UI thread. Note that 
      // code will be executed asynchronously then local 
      // objects may have been disposed! 
      BeginInvoke(new MethodInvoker(delegate 
      { 
       textBox1.Text += "."; 
      })); 
     } 
    }); 
} 

С помощью этого вспомогательного метода можно скрыть шаблон try/wait для сетевых операций:

private static void ProcessResponse(string url, Action<WebResponse> action) 
{ 
    for (int i=1; i <= NumberOfRetries; ++i) 
    { 
     try 
     { 
      var request = WebRequest.Create(line); 

      using (var response = request.GetResponse()) 
      { 
       action(response); 
      } 

      break; 
     } 
     catch (Exception e) 
     { 
      if (i == NumberOfRetries) 
       throw; 

      Thread.Sleep(DelayOnRetry); 
     } 
    } 
} 

private const int NumberOfRetries = 3; 
private const int DelayOnRetry = 1000; 
+0

Я бы также подумал о том, как отключить прокси-поиск (если это необходимо) и отключить NAGLE. – Lloyd

-1

Поскольку вы не указываете версию фреймворка, я предполагаю, что вы используете не менее 4,5.

Вы можете использовать ActionBlock, чтобы легко выполнять несколько вызовов одновременно. ActionBlock выполняет свой метод действий в одном потоке, и несколько исполнений могут выполняться одновременно.

Вы могли бы использовать что-то вроде этого:

var options=new ExecutionDataflowBlockOptions 
{ 
    MaxDegreeOfParallelism = 10 
} 

var block=new ActionBlock<string>(url=> 
{ 
    using(var req = WebRequest.Create(url)) 
    using(var res = req.GetResponse()) 
    { 
     //Process the response here 
    } 
}); 

string[] lines = File.ReadAllLines(@"c:\data\temp.txt"); 
foreach(var line in lines) 
{ 
    block.Post(line); 
} 

block.Complete(); 

await block.Completion; 

Вы можете контролировать, сколько запросов делаются одновременно путем изменения метода MaxDegreeOfParallelism.

Вы также можете позвонить GetResponseAsync для выполнения запроса асинхронно. Это не ускорит их работу, но уменьшит количество потоков ThreadPool, используемых для обслуживания одинакового количества запросов.Это означает, что меньше CPU теряется при блокировке и переключении контекста.

var block=new ActionBlock<string>(url=>async 
{ 
    using(var req = WebRequest.Create(url)) 
    using(var res = await req.GetResponseAsync()) 
    { 
     //Process the response here 
    } 
}); 

Располагая запросов и ответов является важным. Если вы не настроите ответ, соединение с сервером остается активным. .NET применяет ограничение 2 одновременных запросов на домен (т. Е. URL), поэтому сироты могут вызвать задержки до тех пор, пока сборщик мусора не начнет их собирать. Пока вы можете переопределить предел, лучше всегда удалять ответы.

0

Я собираюсь предложить вам использовать Reactive Framework для Microsoft. NuGet «Rx-Main», «Rx-WinForms»/«Rx-WPF».

Вот что код будет выглядеть следующим образом:

private void button4_Click(object sender, EventArgs e) 
{ 
    var query = 
     from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable() 
     from result in Observable.Defer(() => 
     { 
      var req = WebRequest.Create(line); 
      return 
       Observable.Using(
        () => req.GetResponse(), 
        res => Observable.Using(
         () => new StreamReader(res.GetResponseStream(), Encoding.ASCII), 
         st => Observable.Start(() => st.ReadToEnd()))); 
     }) 
     select new { line, result }; 

    query 
     .ObserveOn(textBox1) 
     .Subscribe(x => textBox1.Text += "."); 
} 

Я предположил, что вы пытаетесь прочитать строку из потока.

Этот код прекрасно использует все промежуточные объекты. Он также правильно многопоточно обрабатывает запросы, и он выводит результаты в поток пользовательского интерфейса и обновляет текст текстового поля.

Немного уборщик версия этого кода заключается в следующем:

private void button4_Click(object sender, EventArgs e) 
{ 
    var query = 
     from line in File.ReadAllLines(@"c:\data\temp.txt").ToObservable() 
     from result in Observable.Using(
      () => new WebClient(), 
      wc => Observable.Start(() => wc.DownloadString(new Uri(line)))) 
     select new { line, result }; 

    query 
     .ObserveOn(textBox1) 
     .Subscribe(x => textBox1.Text += "."); 
} 

Он использует WebClient для загрузки. Он по-прежнему многопоточен по мере необходимости.

Смежные вопросы