Хобрук: Ваш путь к мастерству в программировании

Есть ли способ приостановить обсуждение на неопределенный срок?

В свободное время я работал над приложением .NET для сканирования веб-страниц, и одной из функций этого приложения, которую я хотел включить, была кнопка паузы для приостановки определенного потока.

Я относительно новичок в многопоточности, и я не смог найти способ приостановить поток на неопределенный срок, который в настоящее время поддерживается. Я не могу вспомнить точный класс / метод, но я знаю, что есть способ сделать это, но он был отмечен платформой .NET как устаревший.

Есть ли хороший универсальный способ приостановить рабочий поток на неопределенный срок в C # .NET.

В последнее время у меня не было много времени работать над этим приложением, и в последний раз я касался его в среде .NET 2.0. Я открыт для любых новых функций (если таковые имеются), которые существуют в платформе .NET 3.5, но я хотел бы знать решение, которое также работает в структуре 2.0, поскольку это то, что я использую на работе, и было бы хорошо знать на всякий случай.

27.09.2008

Ответы:


1

Никогда, никогда не используйте Thread.Suspend. Основная проблема заключается в том, что в 99% случаев вы не можете знать, что делает этот поток, когда вы его приостанавливаете. Если этот поток удерживает блокировку, вам будет проще попасть в ситуацию взаимоблокировки и т. Д. Имейте в виду, что код, который вы вызываете, может получать / снимать блокировки за кулисами. Win32 имеет аналогичный API: SuspendThread и ResumeThread. Следующие документы для SuspendThread дают хорошее резюме опасностей API:

http://msdn.microsoft.com/en-us/library/ms686345(VS.85).aspx

Эта функция в первую очередь предназначена для использования отладчиками. Он не предназначен для синхронизации потоков. Вызов SuspendThread в потоке, которому принадлежит объект синхронизации, такой как мьютекс или критическая секция, может привести к тупиковой ситуации, если вызывающий поток пытается получить объект синхронизации, принадлежащий приостановленному потоку. Чтобы избежать этой ситуации, поток в приложении, который не является отладчиком, должен сигнализировать другому потоку о приостановке. Целевой поток должен быть спроектирован так, чтобы отслеживать этот сигнал и соответствующим образом реагировать.

Правильный способ приостановить поток на неопределенный срок - использовать ManualResetEvent. Скорее всего, поток зацикливается, выполняя некоторую работу. Самый простой способ приостановить поток - заставить поток «проверять» событие на каждой итерации, например:

while (true)
{
    _suspendEvent.WaitOne(Timeout.Infinite);

    // Do some work...
}

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

Вы бы создали событие так:

ManualResetEvent _suspendEvent = new ManualResetEvent(true);

Параметр true сообщает событию, что оно должно начинаться в сигнальном состоянии.

Если вы хотите приостановить поток, вы делаете следующее:

_suspendEvent.Reset();

И чтобы возобновить обсуждение:

_suspendEvent.Set();

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

Ради интереса я приведу полный пример:

public class Worker
{
    ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
    ManualResetEvent _pauseEvent = new ManualResetEvent(true);
    Thread _thread;

    public Worker() { }

    public void Start()
    {
        _thread = new Thread(DoWork);
        _thread.Start();
    }

    public void Pause()
    {
        _pauseEvent.Reset();
    }

    public void Resume()
    {
        _pauseEvent.Set();
    }

    public void Stop()
    {
        // Signal the shutdown event
        _shutdownEvent.Set();

        // Make sure to resume any paused threads
        _pauseEvent.Set();

        // Wait for the thread to exit
        _thread.Join();
    }

    public void DoWork()
    {
        while (true)
        {
            _pauseEvent.WaitOne(Timeout.Infinite);

            if (_shutdownEvent.WaitOne(0))
                break;

            // Do the work here..
        }
    }
}
27.09.2008

2

Электронная книга Threading в C # резюмирует Thread.Suspend и Thread.Resume таким образом:

У устаревших методов приостановки и возобновления есть два режима - опасный и бесполезный!

В книге рекомендуется использовать конструкцию синхронизации, такую ​​как AutoResetEvent или Monitor.Wait, чтобы выполнить приостановку и возобновление потока.

27.09.2008
  • Мне нужно пойти и прочитать эту книгу. У меня никогда не было проблем с приостановкой / возобновлением. 27.09.2008

  • 3

    Я только что реализовал класс LoopingThread, который зацикливает действие, переданное конструктору. Это основано на сообщении Брэннона. Я добавил в это свойство WaitForPause(), WaitForStop() и TimeBetween, которое указывает время, которое следует подождать до следующего цикла.

    Я также решил изменить цикл while на цикл do-while. Это даст нам детерминированное поведение для последовательных Start() и Pause(). Под детерминированным я подразумеваю, что действие выполняется хотя бы один раз после команды Start(). В реализации Браннона это может быть не так.

    Я упустил некоторые вещи, которые объясняют суть проблемы. Такие вещи, как «проверьте, был ли поток уже запущен» или шаблон IDisposable.

    public class LoopingThread
    {
      private readonly Action _loopedAction;
      private readonly AutoResetEvent _pauseEvent;
      private readonly AutoResetEvent _resumeEvent;
      private readonly AutoResetEvent _stopEvent;
      private readonly AutoResetEvent _waitEvent;
    
      private readonly Thread _thread;
    
      public LoopingThread (Action loopedAction)
      {
        _loopedAction = loopedAction;
        _thread = new Thread (Loop);
        _pauseEvent = new AutoResetEvent (false);
        _resumeEvent = new AutoResetEvent (false);
        _stopEvent = new AutoResetEvent (false);
        _waitEvent = new AutoResetEvent (false);
      }
    
      public void Start ()
      {
        _thread.Start();
      }
    
      public void Pause (int timeout = 0)
      {
        _pauseEvent.Set();
        _waitEvent.WaitOne (timeout);
      }
    
      public void Resume ()
      {
        _resumeEvent.Set ();
      }
    
      public void Stop (int timeout = 0)
      {
        _stopEvent.Set();
        _resumeEvent.Set();
        _thread.Join (timeout);
      }
    
      public void WaitForPause ()
      {
        Pause (Timeout.Infinite);
      }
    
      public void WaitForStop ()
      {
        Stop (Timeout.Infinite);
      }
    
      public int PauseBetween { get; set; }
    
      private void Loop ()
      {
        do
        {
          _loopedAction ();
    
          if (_pauseEvent.WaitOne (PauseBetween))
          {
            _waitEvent.Set ();
            _resumeEvent.WaitOne (Timeout.Infinite);
          }
        } while (!_stopEvent.WaitOne (0));
      }
    }
    
    04.07.2012

    4

    Если нет требований к синхронизации:

    Thread.Sleep(Timeout.Infinite);

    04.11.2016

    5

    Помимо предложений выше, я хотел бы добавить один совет. В некоторых случаях использование BackgroundWorker может упростить ваш код (особенно когда вы используете анонимный метод для определения DoWork и других его событий).

    27.09.2008

    6

    В соответствии с тем, что сказали другие - не делайте этого. Что вы действительно хотите сделать, так это «приостановить работу» и позволить вашим потокам свободно перемещаться. Не могли бы вы подробнее рассказать о цепочках, которые хотите приостановить? Если вы не запускали ветку, вам определенно не стоит даже думать о ее приостановке - она ​​не ваша. Если это ваш поток, то я предлагаю вместо того, чтобы приостанавливать его, просто оставьте его в покое, ожидая, пока будет проделана дополнительная работа. В своем ответе Брэннон предлагает несколько отличных предложений по этому поводу. Или просто позвольте этому закончиться; и запускайте новый, когда вам это нужно.

    14.10.2008

    7

    Suspend () и Resume () могут быть лишены смысла, однако они никоим образом не бесполезны. Если, например, у вас есть поток, выполняющий длительную работу по изменению данных, и пользователь желает остановить его, он нажимает кнопку. Конечно, вам нужно запросить подтверждение, но в то же время вы не хотите, чтобы этот поток продолжал изменять данные, если пользователь решит, что он действительно хочет прервать выполнение. Приостановка потока в ожидании нажатия пользователем кнопки «Да» или «Нет» в диалоговом окне подтверждения - это единственный способ предотвратить изменение данных, прежде чем вы сигнализируете о назначенном событии прерывания, которое позволит ему останавливаться. События могут быть удобны для простых потоков, имеющих один цикл, но еще одна проблема - сложные потоки со сложной обработкой. Конечно, Suspend () не следует никогда использовать для синхронизации, поскольку ее полезность не для этой функции.

    Только мое мнение.

    12.01.2012
    Новые материалы

    Понимание СТРУКТУРЫ ДАННЫХ И АЛГОРИТМА.
    Что такое структуры данных и алгоритмы? Термин «структура данных» используется для описания того, как данные хранятся, а алгоритм используется для описания того, как данные сжимаются. И данные, и..

    Как интегрировать модель машинного обучения на ios с помощью CoreMl
    С выпуском новых функций, таких как CoreML, которые упростили преобразование модели машинного обучения в модель coreML. Доступная модель машинного обучения, которую можно преобразовать в модель..

    Создание успешной организации по науке о данных
    "Рабочие часы" Создание успешной организации по науке о данных Как создать эффективную группу по анализу данных! Введение Это обзорная статья о том, как создать эффективную группу по..

    Технологии и проблемы будущей работы
    Изучение преимуществ и недостатков технологий в образовании В быстро меняющемся мире технологии являются решающим фактором в формировании будущего работы. Многие отрасли уже были..

    Игорь Минар из Google приедет на #ReactiveConf2017
    Мы рады сообщить еще одну замечательную новость: один из самых востребованных спикеров приезжает в Братиславу на ReactiveConf 2017 ! Возможно, нет двух других кланов разработчиков с более..

    Я собираюсь научить вас Python шаг за шагом
    Привет, уважаемый энтузиаст Python! 👋 Готовы погрузиться в мир Python? Сегодня я приготовил для вас кое-что интересное, что сделает ваше путешествие более приятным, чем шарик мороженого в..

    Альтернатива шаблону исходящих сообщений для архитектуры микросервисов
    Познакомьтесь с двухэтапным сообщением В этой статье предлагается альтернативный шаблон для папки Исходящие : двухэтапное сообщение. Он основан не на очереди сообщений, а на..