G
I don't know how. Parallel.For I know how. async/await♪In order to achieve these multi-point tasks with a fixed number of flows, better use is made of a competitive collection based on Produser/Consumer technology. I mean, one flow can throw something in this collection, and the other, once there's something in it, takes it to work. One of the options for such collections is System.Collections.Concurrent.BlockingCollection♪ Her thing is, while she's empty, she's blocking the flow waiting for a new element.To maximize productivity and use https://docs.microsoft.com/ru-ru/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap I recommend that, for the sake of urgency, System.Net.Http.HttpClient♪ At NET Core 3.1, by the way, he's eatin', and he's very good at productivity, so he's a snooper, or what he's driving depends on the type of operating system and a fraimvor.In order to send a query from one queue to a few proxies on the basis of "who took it to work," you can use that class.// чтобы при обработке результатов было понятно, на какой именно запрос получен этот ответ
public struct JobResult
{
public string Url { get; set; }
public string Result { get; set; }
}
public class ProxyWorker
{
private readonly CancellationTokenSource _cts; // для остановки воркера
public WebProxy Proxy { get; } // публично видимые настройки прокси
public ProxyWorker(WebProxy proxy, BlockingCollection<string> jobs, BlockingCollection<JobResult> jobResults)
{
Proxy = proxy;
_cts = new CancellationTokenSource();
// запускаем разгребание задач
Task.Run(() => JobsConsumer(proxy, jobs, jobResults, _cts.Token).GetAwaiter().GetResult());
// или так можно
//Task.Run(async () => await JobsConsumer(proxy, jobs, jobResults, _cts.Token));
}
private async Task JobsConsumer(BlockingCollection<string> jobs, BlockingCollection<JobResult> jobResults, CancellationToken token)
{
using (HttpClient client = new HttpClient(new HttpClientHandler { Proxy = Proxy }, true))
{
try
{
foreach (string url in jobs.GetConsumingEnumerable(token))
{
try
{
// устанавливаем соединение с сервером и читаем заголовок ответа
HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false);
response.EnsureSuccessStatusCode(); // бросить исключение, если 404, 403 и т.д.
Task<string> responseTask = response.Content.ReadAsStringAsync(); // запускаем асинхронно процесс скачивания ответа
await Task.Delay(2000, token).ConfigureAwait(false); // и засекаем 2 секунды
jobResults.Add(new JobResult() { Url = url, Result = await responseTask }); // если ответ еще не доехал, здесь его еще подождем
}
catch (HttpRequestException ex)
{
Debug.WriteLine(ex.Message);
jobs.Add(url); // задача не выполнена, вернем ее в очередь
// быть может не стоит возвращать, или ограничить количество
// попыток, и если оно достигло порога, то не возвращать
}
}
}
catch (OperationCanceledException)
{
// сюда попадаем, если вызван _cts.Cancel()
}
finally
{
_cts.Dispose();
}
}
}
public void Shutdown() // остановить всё, завершить работу воркера
{
_cts.Cancel();
}
}
And all this can be used like this:// Создать рабочие коллекции
BlockingCollection<string> jobs = new BlockingCollection<string>();
BlockingCollection<JobResult> jobResults = new BlockingCollection<JobResult>();
// список воркеров
List<ProxyWorker> workers = new List<ProxyWorker>();
// добавить новый воркер
WebProxy proxy = new WebProxy("http://myproxy.net:3128");
ProxyWorker proxyWorker = new ProxyWorker(proxy, jobs, jobResults);
workers.Add(proxyWorker);
// найти воркер по настройкам прокси
ProxyWorker worker = workers.FirstOrDefault(x => x.Proxy.Address.ToString() == "http://myproxy.net:3128");
// и если найден, то завершить и удалить из списка
if (worker != null)
{
worker.Shutdown();
workers.Remove(worker);
}
// или сразу все воркеры завершить
foreach (ProxyWorker worker in workers)
{
worker.Shutdown();
}
workers.Clear();
// вот так можно закинуть в работу ваши данные
foreach (string elem in data)
{
// пока они закидываются, прокси воркеры уже начали это пережевывать
jobs.Add("xxx.ru?param=" + elem);
}
// а получать ответы можно вот так
int resultsCount = 0;
foreach (JobResult result in jobResults.GetConsumingEnumerable())
{
// тело цикла будет выполняться по мере получения ответов
// так что можно красиво обновлять статус их получения :)
string s = result.Result; // что-то можно сделать с результатом
resultsCount++;
if (resultsCount == data.Count) break; // все ответы получены
}
As a bonus here, it already supports the simultaneous rolling of requests in turn and the simultaneous unloading of replies. There is an unresolved problem in the code mentioned above: url, he'll be cycling back in line, and the job will never end, the comment in the code is above it.Anyway, try and work it out.