C # WPF Threading, как правильно остановить - PullRequest
3 голосов
/ 14 июня 2011

Так что я играл с потоками в течение последних нескольких месяцев, и хотя мой результат такой, как и ожидалось, у меня есть ощущение, что я делаю это не лучшим образом.Кажется, я не могу получить прямой ответ от кого-то, с кем я работаю, о том, что является лучшей практикой, поэтому я подумал, что спрошу вас, ребята.

Вопрос: Я постараюсь сделать это простым, так что терпитемне.Скажем, у меня есть форма с кнопкой запуска и остановки.Кнопка запуска срабатывает и событие запускает поток.Внутри этого потока DoWork он будет вызывать 3 метода.Method1 () печатает на консоль «A \ n» 10 раз с паузой в 10 секунд между ними.Method2 () и Method3 () - это одно и то же, просто разные буквы и разное время паузы между Console.WriteLine.Теперь, когда вы нажимаете кнопку «Стоп», вы хотите, чтобы ответ был немедленным.Я не хочу ждать завершения методов.Как я могу это сделать?

То, как я это делал, передает мой BackgroundWorker каждому методу и проверяет работника. Отмена отложенных транзакций выглядит так

 public void Method1(BackgroundWorker worker)
    {
      for(int i = 0; i < 10 && !worker.CancellationPending; ++i)
      {
        Console.WriteLine("A");
        for(int j = 0; j < 100 && !worker.CancellationPending; ++i)
        {
          Thread.Sleep(100);
        }
      }
    }

Как я уже сказал, это дает мнежелаемый результат, однако, представьте, что method1 становится намного более сложным, скажем, он использует DLL для записи, которая имеет keydown и key up.Если бы я просто прервал поток, я мог бы также оставить себя в нежелательном состоянии.Я засоряю свой код !worker.CancellationPending.Практически в каждом блоке кода я проверяю CancellationPending.Я смотрю на множество примеров в сети и редко вижу, как люди передают поток, как я.Что такое лучшие практики по этому вопросу?

Ответы [ 3 ]

1 голос
/ 14 июня 2011

Подумайте об использовании итераторов (yield return), чтобы разбить шаги.

  public void Method1(Backgroundworker worker)
  {
     foreach (var discard in Method1Steps)
     {
        if (worker.CancelationPending)
           return;
     }
  }

  private IEnumerable<object> Method1Steps()
  {
     for (int i = 0; i < 10; ++i)
     {
        yield return null;
        Console.WriteLine("A");
        for (int j = 0; j < 100; ++i)
        {
           Thread.Sleep(100);
           yield return null;
        }
     }
  }

Это решение может быть сложнее реализовать, если у вас есть куча try / catch / finally или куча вызовов методов, которыеТакже нужно знать об отмене.

0 голосов
/ 14 июня 2011

Что ж, если вы находитесь в ситуации, когда вам необходимо прервать поток, интенсивно использующий процессор, то вы немного застряли с тестированием логического значения 'Abort' (или токена отмены) в одном или другом цикле (возможно, нет самый внутренний - зависит от того, сколько времени это займет). AFAIK, вы можете просто «вернуться» из внутреннего цикла, поэтому выход из метода - не нужно проверять на каждом уровне! Чтобы минимизировать накладные расходы на это, попытайтесь сделать его локальным логическим значением, то есть стараться не разыменовывать его через полдюжины ...... классов каждый раз.

Может быть, наследуют классы от Stoppable, у которого есть метод Abort и логическое значение Stop? В приведенном выше примере вы проводите большую часть времени в спящем режиме, поэтому средняя задержка составляет 50 мсек, прежде чем что-либо проверять. В таком случае вы можете подождать какое-то событие с таймаутом вместо сна. Переопределите «Прервать», чтобы установить событие, а также вызвать унаследованный «Прервать», поэтому завершите ожидание раньше. Вы также можете установить событие в делегате / обратном вызове cancellationToken, если вы реализуете эту новую функциональность, как описано Дэном.

На самом деле существует очень мало Windows API и т. Д., Которые нелегко «не пометить» или не имеют асинхронных версий «Ex», поэтому ошибаться… «почти» всегда можно отменить, так или иначе, например , закрытие сокетов для принудительного чтения сокетов, кроме, запись временного файла для возврата уведомлений об изменениях папок.

Rgds, Martin

0 голосов
/ 14 июня 2011

Да, вы делаете это правильно. Сначала это может показаться неловким, но это действительно лучший вариант. Это определенно намного лучше, чем прерывание потока. Как вы обнаружили, итерации цикла являются идеальными кандидатами для проверки CancelationPending. Это потому, что итерация цикла часто изолирует логическую единицу работы и, таким образом, легко определяет безопасную точку. Безопасные точки - это маркеры выполнения потока, в котором завершение может быть легко выполнено без повреждения каких-либо данных.

Хитрость заключается в том, чтобы опросить CancelationPending в безопасных точках достаточно часто, чтобы своевременно сообщить вызывающему абоненту, что отмена успешно завершена, но не слишком часто, чтобы негативно повлиять на производительность или «засорять код».

В вашем конкретном случае внутренний цикл - лучшее место для опроса CancelationPending. Я бы пропустил проверку на внешнем цикле. Причина в том, что внутренняя петля - то, где большую часть времени проводят. Проверка на внешнем цикле была бы бессмысленной, потому что внешний цикл выполняет очень мало фактической работы, за исключением запуска внутреннего цикла.

Теперь на стороне графического интерфейса пользователя вы можете захотеть выделить серым кнопку «Стоп», чтобы сообщить пользователю, что запрос на отмену принят. Вы можете отобразить сообщение типа «отмены отмены» или тому подобное, чтобы прояснить ситуацию. Получив отзыв о завершении отмены, вы можете удалить сообщение.

...