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

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

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

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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
33
0
36 500
7
Перейти к ответу Данный вопрос помечен как решенный

Ответы 7

Электронная книга Многопоточность в C# резюмирует Thread.Suspend и Thread.Resume следующим образом:

The deprecated Suspend and Resume methods have two modes – dangerous and useless!

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

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

Phil Wright 27.09.2008 07:02
Ответ принят как подходящий

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

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

This function is primarily designed for use by debuggers. It is not intended to be used for thread synchronization. Calling SuspendThread on a thread that owns a synchronization object, such as a mutex or critical section, can lead to a deadlock if the calling thread tries to obtain a synchronization object owned by a suspended thread. To avoid this situation, a thread within an application that is not a debugger should signal the other thread to suspend itself. The target thread must be designed to watch for this signal and respond appropriately.

Правильный способ приостановить поток на неопределенный срок - использовать 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..
        }
    }
}

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

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

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

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

Я только что реализовал класс 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));
  }
}

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

Thread.Sleep(Timeout.Infinite);

Другие вопросы по теме