Каков наилучший способ ожидания потока? - PullRequest
0 голосов
/ 07 ноября 2019

Иногда вы получаете сценарий, в котором поток должен ждать, пока он не получит сигнал от другого потока. в настоящее время я достигаю этого следующими способами: (существует volatile int signal;, который установлен другим потоком, чтобы сообщить ожидающему потоку продолжение)

Метод 1: Инстинктивно самая низкая задержка, но самое высокое использование процессора в то время какожидание:

while(signal != 1);

Метод 2: Все еще использует 100% процессор / ядро, но я думаю, что лучше, если другие потоки могут работать? все еще очень низкая задержка

while(signal != 1) Sleep(0);

Метод 3: приводит к незначительному использованию процессора во время ожидания (сообщает как 0% в диспетчере задач), но, очевидно, имеет задержку 1 мс.

while(signal != 1) Sleep(1);

Есть ли лучшие способы справиться с этим делом? В первую очередь интересуюсь родным c ++ Win32, но также интересуюсь linux / android / ios.

Если не существует волшебного решения по использованию процессора с низкой задержкой / низким ожиданием, меня интересует решение с самой низкой задержкой в ​​обоихследующие случаи: ожидание использования процессора не имеет значения, и ожидание использования процессора должно быть незначительным.

Извинения, если это немного нубский вопрос, но я новичок в такого рода вещах.

1 Ответ

2 голосов
/ 07 ноября 2019

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

Прямо сейчас, вы будете заняты ожиданием - то есть, пока вы ждете,вы в какой-то степени удерживаете (хотя бы одно ядро) ЦП, проверяя, может ли он еще работать.

Выполнение Sleep(0) в основном возвращает текущий поток на назад из очереди потоков, готовых к выполнению и ожидающих выполнения их процессором. Таким образом, если никакой другой поток не ожидает, он все равно будет потреблять 100% процессорного времени. С другой стороны, если другие потоки с таким же приоритетом будут готовы к запуску, они получат шанс на запуск до того, как этот поток будет снова запланирован.

Хотя одно замечание: volatile на самом деле не определенодля связи между потоками, как это, поэтому вы хотите вместо этого использовать атомарную переменную.

std::atomic<bool> signal { false };

while (!signal)
    Sleep(0); // or Sleep(1)

Это может быть хорошо , если вы ожидаете, что это займет всего пару миллисекунд (или что-то в этом порядке) до того, как signal станет true, чтобы поток мог продолжить. Так как он не ждет очень долго, он не использует много процессорного времени во время ожидания и (особенно если система слегка загружена) может очень быстро реагировать, когда signal становится true.

Если поток, вероятно, будет ждать дольше, чем несколько миллисекунд, вам, вероятно, лучше воспользоваться механизмом, который берет текущий поток и помечает его как не готовый к запуску, но вместо этого ожидает некоторый объект ядра, прежде чем он сможет / будетзапланировано снова. Windows предоставляет Event s для этой цели:

HANDLE signal = CreateEvent(/* ... */);

WaitForSingleObject(signal, INFINITE);

Когда другой код хочет сообщить, что этот поток должен работать, он делает что-то вроде:

SetEvent(signal);

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

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

std::atomic<bool> signal;
std::mutex m;
std::condition_variable v;

std::unique_lock<std::mutex> lock(m);
v.wait(lock, [&] { return signal; });

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

std::unique_lock<std::mutex> lock(m);
signal = true;
v.notify_one();

Переменная условия имеет свои преимущества, но она не самая простая в использовании. С другой стороны, как только вы привыкнете к шаблону, это тоже не составит особого труда.

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