#опытным
Более менее все знакомы с примитивом синхронизации "условная переменная". При наличии мьютекса она может погрузить поток в сон до тех пор, пока не поступит уведомлении и условие пробуждение не будет истинным. Сильно упрощая, в консюмере ждем события на wait, а в продюсере уведомляем через notify о возникновении события.
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void consumer() {
std::unique_lock lock(mtx);
cv.wait(lock, []{ return ready; }); // Wait until 'ready' is true
// Proceed with work
}
void producer() {
{
std::lock_guard<std::mutex> lock(mtx);
ready = true;
}
cv.notify_one(); // Wake up one waiting thread
}
Но как это работает под катопом?
Если задуматься, то кондвар очень похож на семафор и мьютекс. По сути оба отправляют поток спать, когда счетчик пытаются декрементить ниже 0. И они оба уведомляют одного из ожидающих, когда счетчик становится равным 1 или больше. Да, семантика разная, но факт на лицо: есть и ожидание и уведомление.
Так может у кондваров и мьютексов/семафоров есть общие инструменты реализации?
Бинго!
На линуксе есть такая штука - futex. Fast Userspace muTEX. Это низкоуровневый примитив синхронизации в ядре. Главная идея: быстрый путь (захват/освобождение без конкуренции) делается в userspace обычными атомарными операциями. Ядро вызывается только когда нужно усыпить или разбудить поток.
Если надо усыпить поток - он помещается в очередь ожидания. Если надо разбудить поток - он достается из головы очереди и продолжает исполнение.
Так вот кондвары и реализованы на основе такой очереди.
Конечно, нужно довольно много всего построить вокруг помещения потока в ожидание, но принцип такой.
При вызове wait поток освобождает переданный мьютекс и засыпает(вызывается условный futex_wait), помещаясь в очередь ожидания. При уведомлении (вызов futex_wake) о наступлении условия поток достается из очереди, будится и конкурирует за захват мьютекса, чтобы честно проверить верность условия(может быть stolen wakeup и поток обратно уйдет спать).
Прямым аналогом futex в Windows является семейство функций
WaitOnAddress и WakeByAddressSingle/WakeByAddressAll.Don't busy wait. Stay cool.
#concurrency #OS