Потоки, количество и правильный их расчет в работе
вопрос по поводу потоков, работаю с Post запросами (туда-обратно с параметрами) на внешний ресурс, потоки запускаю вот так: *Опыта 1 день в данной теме, не судите строго если что не так.
Threads = new Thread[100];
for (int i = 0; i < CountThreads; i++) {
Threads[i] = new Thread(Program.Checking);
Threads[i].IsBackground = true;
Threads[i].Start();
}
Для примера я выполняю 400 запросов, с определенным значением CountThreads (1,6,300) вот временные характеристики скорости работы:
Сервер же намертво отключается при более 100 потоков (Обрабатывает только мои запросы из вне), вот и сам вопрос, я правильно понимаю что причина отсутствия увеличения скорости при увеличении количества потоков - то что сервер не успевает обрабатывать запросы или все-таки вероятнее код не оптимален? Порадует любая инфа.
Добавлю, при выполнении идентичного php скрипта с одного сервера Post запросом на другой сервер: Время выполнения скрипта: 53.8089 сек. (400 выполнений) При выполнении кода просто циклом в static void Main скорость примерно как при 6'ти потоках.
*Прикрепляю
public static void CreateThreads()
{
IsWork = true;
Threads = new Thread[500];
for (int i = 0; i < CountThreads; i++)
{
if (i > CountThreads)
{
break;
}
Threads[i] = new Thread(Program.Checking);
Threads[i].IsBackground = true;
Threads[i].Start();
}
}
static private void Checking()
{
while (IsWork)
{
string response = "";
try
{
ird++;
response = UseGo(ird);
}
catch { }
Thread.Sleep(50);
}
}
static string UseGo(int irt)
{
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
httpWebRequest.ContentType = "application/json";
httpWebRequest.Method = "POST";
using (var streamWriter = new StreamWriter(httpWebRequest.GetRequestStream()))
{
string json = " [ Данные JSON ] ";
streamWriter.Write(json);
streamWriter.Flush();
streamWriter.Close();
}
var httpResponse = (HttpWebResponse)httpWebRequest.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
var result = streamReader.ReadToEnd();
Console.WriteLine("[Process Thread] Complite: " + irt);
return result;
}
}
И вызываю в Main вот так:
CreateThreads();
Console.ReadLine();
Ответы (1 шт):
Вы используете
HttpWebRequest, который устарел, и для .NET Core совершенно нет причин его использовать, вместо него естьHttpClient. К тому же он умеет переиспользовать открытые TCP соединения, что ускоряет время "запрос-ответ" для последующих запросов на один и тот же сервер.Создание нового потока
Thread- тоже очень дорогая штука, но есть очень удобный классTask, который умеет переиспользовать потоки из пула потоков, и к тому же позволяет писать асинхронный код с помощьюasync/await.Для преобразования Json в объекты и наоборот, в .NET Core существует отличный парсер
System.Text.Json, кстати сравнительно молодой, но весьма шустрый.
Почитайте про все про это, а я просто покажу вам пример реализации того, что вы делаете с помощью всего вышеперечисленного.
Классы для запроса и ответа, наполните их необходимыми свойствами. Запрос будет читаться из класса-источника, ответ соответсвенно будет заноситься в экземпляр класс-приемника путем Json сереиализации и десереализации.
public class JsonRequestData
{
[JsonPropertyName("param1")]
public string Param1 { get; set; }
[JsonPropertyName("param2")]
public string Param2 { get; set; }
}
public class JsonResponseData
{
[JsonPropertyName("status")]
public string Status { get; set; }
}
Я создал вот такой простой класс, ну чтобы не париться со статическими полями и методами в классе Program.
В методе SendHttpPost я собрал всю сетевую работу и преобразования. Наверняка есть способы проще и понятнее, комментаторы меня поправят, но писал на коленке и не тестировал, так как не имею сейчас подходящего сервера, принимающего POST запросы.
В методе Run реализовал алгоритм по запуску общего количества запросов с ограничением на количество одновременно выполняющихся.
public class MyHttpManager
{
private readonly HttpClient client = new HttpClient();
private readonly JsonSerializerOptions serializeOptions = new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping };
private readonly JsonSerializerOptions deserializeOptions = new JsonSerializerOptions { ReadCommentHandling = JsonCommentHandling.Skip };
public async Task Run()
{
JsonRequestData jsonRequestData = new JsonRequestData();
jsonRequestData.Param1 = "val1";
jsonRequestData.Param2 = "val2";
int numRequests = 400; // сколько запросов отправить
int maxConcurrentTasks = Environment.ProcessorCount * 2; // сколько одновременно работающих задач
SemaphoreSlim semaphore = new SemaphoreSlim(maxConcurrentTasks);
List<Task> tasks = new List<Task>();
for (int i = 0; i < numRequests; i++)
{
await semaphore.WaitAsync(); // ждать, если активно максимальное число задач, пока место не освободится
tasks.Add(Task.Run(async () =>
{
JsonResponseData jsonResponseData = await SendHttpPost<JsonResponseData>("https://example.org/api/method", jsonRequestData);
Console.WriteLine(jsonResponseData?.Status ?? "[Task] Null response");
semaphore.Release();
}));
}
await Task.WhenAll(tasks); // ждать завершения всех задач
}
private async Task<T> SendHttpPost<T>(string url, object data) where T : class
{
try
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
string text = JsonSerializer.Serialize(data, serializeOptions);
Console.WriteLine(text);
using HttpContent content = new StringContent(text, Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.PostAsync(url, content).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
using Stream responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
T result = await JsonSerializer.DeserializeAsync<T>(responseStream, deserializeOptions);
stopwatch.Stop();
Console.WriteLine("[Task] Complete: " + stopwatch.ElapsedMilliseconds + "ms");
return result;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return null;
}
}
}
И вот так это все стартует, обратите внимание на асинхронный Main
public class Program
{
static async Task Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
MyHttpManager manager = new MyHttpManager();
await manager.Run();
stopwatch.Stop();
Console.WriteLine("[Main] All complete: " + stopwatch.ElapsedMilliseconds + "ms");
Console.ReadKey();
}
}
Разберите пример, попробуйте его заставить работать в ваших условиях.
Кстати, вот результат сериализации экземпляра класса JsonRequestData, использованного в коде выше.
{
"param1": "val1",
"param2": "val2"
}
Как достать string из ответа. Для этого надо его прочитать полностью, а я передаю сразу поток получения в десереализацию.
Можно вот этот фрагмент
using Stream responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
T result = await JsonSerializer.DeserializeAsync<T>(responseStream, deserializeOptions);
Заменить вот на этот, это правда будет кушать немного больше памяти, так как получаемый ответ будет читаться в память полностью.
string responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
// responseText здесь - то что вам нужно.
T result = JsonSerializer.Deserialize<T>(responseText, deserializeOptions);
