Подтвердить что ты не робот

Есть ли способ бесконечно приостановить поток?

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

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

Есть ли какой-либо хороший общий способ бесконечного приостановления рабочего потока в С#.NET.

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

4b9b3361

Ответ 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..
        }
    }
}

Ответ 2

Threading в С# в ebook суммирует Thread.Suspend и Thread.Resume таким образом:

Устаревшие методы Suspend и Resume имеют два режима - опасные и бесполезные!

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

Ответ 3

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

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

Я пропустил некоторые вещи для корня материи. Такие вещи, как "проверить, был ли поток уже запущен" или шаблон 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));
  }
}

Ответ 4

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

Ответ 5

В соответствии с тем, что говорили другие, не делайте этого. То, что вы действительно хотите сделать, это "приостановить работу" и позволить вашим потокам свободно перемещаться. Можете ли вы дать нам более подробную информацию о потоках, которые вы хотите приостановить? Если вы не запустили нить, вы определенно не должны даже рассматривать ее приостановку - ее не ваши. Если это ваша нить, я предлагаю вместо ее приостановить, вы просто сидите, ожидая больше работы. У Брэннона есть отличные рекомендации по этому варианту в его ответе. В качестве альтернативы просто позвольте этому закончить; и разворачивайте новый, когда вам это нужно.

Ответ 6

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

Thread.Sleep(Timeout.Infinite);

Ответ 7

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

Просто мое мнение.