Re: Как правильно приостановить поток?
От: Кодт Россия  
Дата: 30.01.17 10:12
Оценка: 8 (2)
Здравствуйте, RussianFellow, Вы писали:

RF>Вопрос состоит в следующем--правильно ли я делаю, что организовал цикл while ((*threadflag!=0)&&(*threadflag!=1)) и в нём вызываю команду Sleep(1000); для ожидания команды завершить или возобновить поток? Или же можно как-то по-другому это сделать?


1. Делать принудительную самоостановку — Sleep(t) — плохая практика, ибо это приводит к тормозам в другом потоке, который захочет повзаимодействовать с твоим (например, на предмет завершения программы).
Тем более, засыпать с темпом 1 секунда.

2. Вместо активного опроса — while(!condition()) sleep(timeout); — который для маленьких таймаутов приводит к перегреву, а для больших — к тормозам, — следует использвовать условные переменные.
std::mutex guard; // защищает данные, участвующие в вычислении условия
std::condition_variable cv; // следит за изменением данных

enum class Flag { Run, Suspend, Exit };
Flag flag; // эти самые данные

// пример функции слежения
bool wait_after_suspension() {
  std::unique_lock<std::mutex> lock(guard);
  cv.wait(lock, []() { return flag != Flag::Suspend; });
  return flag != Flag::Exit;
}

// пример функции установки
void set_flag(Flag value) {
  std::unique_lock<std::mutex> lock(guard);
  if (flag != v) {
    flag = v;
    cv.notify_all();
  }
}

Ну или что-то в таком роде.
На голом винапи условные переменные появились, если мне не изменяет память, начиная с висты. Для совместимости с более ранними версиями винды можно устроить велосипеды на эвентах.
Но проще взять
#include <thread>
и пусть у компиляторщиков болит голова о правильной безглючной реализации имеющимися средствами.

3. Чтобы два раза не вставать. Ни в коем случае не пытайся тормозить поток извне, с помощью ::SuspendThread. Это очень короткая дорога к дедлокам.
void victim_thread() {
  void* p = malloc(1);  // 1
  free(p);
}
HANDLE hVictim;

void another_thread() {
  void* p = malloc(1); // 2
  free(p); // 3
}

void killer_thread() {
  SuspendThread(hVictim); // в тот момент, когда он был внутри malloc и успел захватить мьютекс менеджера кучи
  // another_thread заходит в 2 или в 3 и засыпает там в очереди перед заблокированным мьютексом
  void* p = malloc(1); // убийца тоже заходит внутрь и засыпает перед заблокированным мьютексом
  free(p);
  ResumeThread(hVictim); // сюда мы никогда не попадём
}
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.