Parallel.ForEach CancellationTokenSource не останавливается

1

В настоящее время я пишу библиотеку ProxyChecker. Я использую Thread, который вызывает цикл Parallel.ForEach для проверки всех прокси. Я использую CancellationTokenSource (cts), чтобы выполнить мягкое прерывание (cts.Cancel()). Как вы можете видеть в следующем коде, я добавил немного "тестового кода", который записывает текущие потоки в консоль.

Вот код, который вам нужен:

private void CheckProxies(string[] proxies, int timeout, int threads, string domainToCheckWith)
        {
            _cts = new CancellationTokenSource();
            int checkedProxyCount = 0, uncheckedProxyCount = proxies.Length, goodProxies = 0, badProxies = 0;
            mainThread = new Thread(() =>
            {
                try
                {
                    Parallel.ForEach(proxies, new ParallelOptions {MaxDegreeOfParallelism = threads, CancellationToken = _cts.Token}, prox =>
                    {
                        Interlocked.Increment(ref running);
                        Console.WriteLine("thread running: {0}", running);
                        try
                        {
                            _cts.Token.ThrowIfCancellationRequested();
                            if (CheckProxy(prox, domainToCheckWith, timeout))
                            {
                                Interlocked.Increment(ref checkedProxyCount);
                                Interlocked.Increment(ref goodProxies);
                                Interlocked.Decrement(ref uncheckedProxyCount);
                            }
                            else
                            {
                                Interlocked.Increment(ref checkedProxyCount);
                                Interlocked.Decrement(ref uncheckedProxyCount);
                                Interlocked.Increment(ref badProxies);
                            }
                            _cts.Token.ThrowIfCancellationRequested();
                            OnUpdate(uncheckedProxyCount, checkedProxyCount, goodProxies, badProxies);
                        }
                        catch (OperationCanceledException ex) {}
                        catch (ObjectDisposedException ex) {}
                        catch (Exception ex)
                        {
                            OnLog(ex.Message, Color.Red);
                        }
                        finally
                        {
                            Console.WriteLine("thread running: {0}", running);
                            Interlocked.Decrement(ref running);
                        }
                    });
                }
                catch (OperationCanceledException ex) {}
                catch (ObjectDisposedException ex) {}
                catch (Exception ex)
                {
                    OnLog(ex.Message, Color.Red);
                }
                finally
                {
                    isRunning = false;
                    OnComplete();
                }
            });
            mainThread.Start();
        }

Выход (я вынул несколько строк, так как это бесполезно, чтобы дать полный код)

thread running: 1
thread running: 1
thread running: 2
thread running: 2

//Slowly going up to  50

thread running: 50
thread running: 50
thread running: 50

//Staying at 50 till I press stop

thread running: 50
thread running: 50
thread running: 50
thread running: 50
thread running: 50
thread running: 49
thread running: 48
thread running: 47
thread running: 46

//Going down...

thread running: 17
thread running: 16
thread running: 15
thread running: 14
thread running: 13
thread running: 12
thread running: 11
thread running: 10
thread running: 10
thread running: 8
thread running: 7
thread running: 6
thread running: 5
thread running: 4

И затем он останавливается на 4 или 3 или 2 (каждый раз каждый раз). Я подождал несколько минут, но он не спустился и код после того, как Parallel.ForEach будет выполнен.

Тайм-аут для запроса - 5000, потоки - 50.

Вот другой код для проверки:

private bool CheckProxy(string proxy, string domainToCheckWith, int timeout)
{
    try
    {
        WebRequest req = WebRequest.Create(domainToCheckWith);
        req.Proxy = new WebProxy(proxy);
        req.Timeout = timeout;
        var response = (HttpWebResponse) req.GetResponse();
        string responseString = ReadResponseString(response);

        if (responseString.Contains("SOMETHING HERE"))
        {
            OnGoodProxy(proxy);
            return true;
        }
        if (responseString.Contains("SOMEOTHERTHINGHERE"))
        {
            OnBadProxy(proxy);
            return false;
        }
        OnBadProxy(proxy);
        return false;
    }
    catch (WebException ex)
    {
        OnBadProxy(proxy);
        return false;
    }
    catch (Exception ex)
    {
        OnLog(ex.Message, Color.Red);
        return false;
    }
}

Функция остановки:

public void StopChecking()
{
    try
    {
        if (_cts != null && mainThread.IsAlive)
        {
            if (_cts.IsCancellationRequested)
            {
                mainThread.Abort();
                OnLog("Hard aborting Filter Threads...", Color.DarkGreen);
                while (mainThread.IsAlive) ;
                OnComplete();
                isRunning = false;
            }
            else
            {
                _cts.Cancel();
                OnLog("Soft aborting Filter Threads...", Color.DarkGreen);
            }
        }
    }
    catch (Exception ex)
    {
        OnLog(ex.Message, Color.Red);
    }
}

ВАЖНОЕ ИЗМЕНЕНИЕ:

Я добавил это к функции CeckProxy:

        Stopwatch sw = new Stopwatch();
        sw.Start();
        string responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
        sw.Stop();

Это результат последних нескольких потоков:

thread running: 6
4449
thread running: 5
72534
thread running: 4
180094
thread running: 3

почему это так долго? Я имею в виду 180 секунд?!

  • 0
    Вы когда-нибудь вызывали _cts.Cancel (). Я нигде этого не вижу.
  • 0
    @brumScouse Я не добавил эту часть, но могу. Я отредактирую ОП через секунду
Показать ещё 6 комментариев
Теги:
parallel-processing
cancellationtokensource

2 ответа

0
Лучший ответ

Хорошо, я понял это сам.

Теперь я читаю ответ непрерывным и проверяю с помощью секундомера (и request.ReadWriteTimeout), что часть чтения останавливается через определенное время (в моем случае readTimeout). Код

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(domainToCheckWith);
            req.Proxy = new WebProxy(proxy);
            req.Timeout = timeout;
            req.ReadWriteTimeout = readTimeout;
            req.Headers.Add(HttpRequestHeader.AcceptEncoding, "deflate,gzip");
            req.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;

            byte[] responseByte = new byte[1024];
            string responseString = string.Empty;

            sw.Start();
            using (WebResponse res = req.GetResponse())
            {
                using (Stream stream = res.GetResponseStream())
                {
                    while (stream.Read(responseByte, 0, responseByte.Length) > 0)
                    {
                        responseString += Encoding.UTF8.GetString(responseByte);
                        if(sw.ElapsedMilliseconds > (long)timeout)
                            throw new WebException();
                    }

                }
            }
            sw.Stop();
0

Вы можете попробовать заблокировать внутри попытаться заблокировать объект

Object lockObject = new Object();
try
{
    Parallel.ForEach(proxies, new ParallelOptions {MaxDegreeOfParallelism = threads, CancellationToken = _cts.Token}, prox =>
    {
        Interlocked.Increment(ref running);
        Console.WriteLine("thread running: {0}", running);
        try
        {
            lock(lockObject)
            {
                //code.............
            }
        }
        catch
        {
        }
    }
}
catch
{
}
  • 2
    Оно работает. Но теперь это уже не настоящая многопоточность, потому что она ожидает завершения предыдущего потока / снятия блокировки! Так что это на самом деле не помогает мне!
  • 0
    Вы должны будете сделать что-то вроде этого ParallelOptions pOptions = new ParallelOptions (); чем внутри блока try catch или, может быть, внутри, если еще вам придется вызывать pOptions.CancellationToken.ThrowIfCancellationRequested (); или у вас также может быть ..., proxy, (i, loopstate) => чем внутри try catch, вам нужно looppstate.Stop ();
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню