Если честно, я не верю, что многопоточность, которую вы пытаетесь сделать, принесет вам какой-то прирост производительности.И, видя, что здесь нет порогового значения для числа потоков, которые вы могли бы создать, существует потенциал для худшей производительности, чем существует однопоточная (последовательная) операция.
Идеальная ситуация заключается в том, что у вас асинхронный рабочий процесс.где ваш цикл выглядит примерно так:
GetAsyncRequest MakeAsyncRequest ReceiveResponseAsync ProcessResponse WaitForAllRequestProcessingToComplete (необязательно)
Такой, что результат каждого шага передается следующему (если есть результат) и следующему.И вы обрабатываете ответы, как только получаете их, а не накапливаете (объединяете / блокируете) все ответы, прежде чем приступить к их обработке.Такого рода вещи можно легко сделать с помощью Tasks и ContinueWith в .NET 4.0 и, видя, что вы используете .NET 4.0, я настоятельно рекомендую вам сделать это, как описано выше.
Но, если выневозможно преобразовать вашу обработку в асинхронный рабочий процесс, а затем ...
Метод, показанный ниже, - это метод, который вызывает URL-адрес и возвращает ответ.В этом методе используются асинхронные вызовы, но он блокируется, поскольку ваш дизайн выглядит таковым.
static string GetWebResponse(string url, NameValueCollection parameters)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
var sb = new StringBuilder();
foreach (var key in parameters.AllKeys)
sb.Append(key + "=" + parameters[key] + "&");
sb.Length = sb.Length - 1;
byte[] requestBytes = Encoding.UTF8.GetBytes(sb.ToString());
httpWebRequest.ContentLength = requestBytes.Length;
using (var requestStream = httpWebRequest.GetRequestStream())
{
requestStream.Write(requestBytes, 0, requestBytes.Length);
requestStream.Close();
}
Task<WebResponse> responseTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
using (var responseStream = responseTask.Result.GetResponseStream())
{
var reader = new StreamReader(responseStream);
return reader.ReadToEnd();
}
}
Вы бы назвали это так:
ServicePointManager.DefaultConnectionLimit = 20;//Please test different numbers here
var tasks = new List<Task<string>>();
for (int i = 1990; i < 2090; i++)
{
var postParameters = new NameValueCollection();
postParameters.Add("data", i.ToString());
tasks.Add(Task.Factory.StartNew(() => { return GetWebResponse("http://www.abc123.com", postParameters); }));
}
Task.WaitAll(tasks.ToArray());
//At this point tasks[0].Result will be the result (The Response) of the first task
//tasks[1].Result will be the result of the second task and so on.
Посмотрите, работает ли это для вас.
Если вам действительно нужны многопоточные возможности, конечно, учитывая, что вы заходите только на один сайт, нужно будет измерить выигрыш в производительности, поскольку сайт должен уметь обрабатывать натиск запросов идля клиента затраты на создание потоков только для выполнения некоторой связанной с вводом / выводом задачи могут оказаться слишком дорогими и закончиться без увеличения производительности.
Кроме того, без настройки DefaultConnectionLimit в ServicePointManager вы никогда не будетеполучить более 2 потоков в любом случае, так как вы идете против одного домена и ограничение по умолчанию составляет 2 потока на домен.
Я бы придерживался кода, который я представил, и если есть только проблемы с производительностьютогда я бы посмотрел на это как-то иначе.
Редактировать: при использовании асинхронного ввода-вывода вы не используете рабочие потоки, ноПоток ввода / вывода.Поэтому в основном вы не хотите использовать QueueUserWorkItem (для создания потоков) или сами не создаете потоки.
Код, который я представил, использует асинхронный ввод-вывод, и если вы собираетесь выполнять несколько запросов одновременнои как можно быстрее.
Цикл for (во втором листинге кода) завершится почти сразу, хотя в этом примере он повторяется в течение 100 итераций, а затем будет ожидать завершения всех запросов ввода-вывода.ThreadPool и ОС будут обрабатывать задания ввода-вывода как можно быстрее и быстрее.Фактически, поскольку эти задания связаны с вводом-выводом, вы также не увидите увеличения загрузки вашего ЦП (если только вы не выполняете работу, связанную с процессором позже).
Просто поиграйте с ServiceManager.DefaultConnectionLimit, чтобы получитьбольше ускорения, если это необходимо.Обратите внимание, что это также влияет на службу (сервер), поскольку, если вы делаете много одновременных запросов, сервер, который вы вызываете, сильно загружается, и это может быть не то, что вам нужно.Таким образом, необходимо соблюсти баланс.
После вызова Task.WaitAll вы можете перебирать свою коллекцию задач и получать результаты каждой задачи, используя синтаксис, показанный в строке с комментариями в листинге кода..