Как часто используются условные переменные в C ++? - PullRequest
15 голосов
/ 19 марта 2010

Я пытаюсь узнать об условных переменных. Я хотел бы знать, каковы общие ситуации, когда используются условные переменные.

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

В каких других ситуациях проектирования вам нужна переменная условия?

Я бы предпочел примеры, основанные на опыте, например, в реальных живых приложениях.

Ответы [ 5 ]

2 голосов
/ 19 марта 2010

Одним из вариантов использования условных переменных, который немного сложнее, чем просто очередь сообщений, является «разделение блокировки», когда разные потоки ожидают слегка разных условий одной и той же базовой природы. Например, у вас есть (очень тонкий, упрощенный) веб-кеш. Каждая запись в кеше имеет три возможных состояния: нет, IN_PROGRESS, COMPLETE.

getURL:
    lock the cache
    three cases for the key:
        not present:
            add it (IN_PROGRESS)
            release the lock
            fetch the URL
            take the lock
            update to COMPLETE and store the data
            broadcast the condition variable
            goto COMPLETE
        COMPLETE:
            release the lock and return the data
        IN_PROGRESS:
            while (still IN_PROGRESS):
                wait on the condition variable
            goto COMPLETE

Я на практике использовал шаблон для реализации варианта функции POSIX pthread_once без помощи планировщика. Причина, по которой я не смог использовать семафор или блокировку для once_control, а просто выполнить инициализацию под блокировкой, заключается в том, что функция не имела права на сбой, а once_control имела только тривиальную инициализацию. В этом отношении, pthread_once сам по себе не имеет определенных кодов ошибок, поэтому реализация его, возможно, не удастся, не оставляет вашего звонящего с хорошими вариантами ...

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

1 голос
/ 19 марта 2010

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

0 голосов
/ 19 марта 2010

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

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

Фоновый поток следует этой базовой логике

void threadFunction() {
    initialisation();

    while(! shutdown()) {
        backgroundTask();

        shutdown_condition_wait(timeout_value);
    }

    cleanup();
}

Это позволяет быстро и аккуратно завершить фоновый поток.

Если у меня есть несколько таких потоков, основная функция подает сигнал на отключение, а затем присоединяется к каждому после следующего. Это позволяет каждому компоненту потока отключаться параллельно.

0 голосов
/ 19 марта 2010

Я использую условные переменные вместо подверженных ошибкам объектов Win32 Event. С condvars вам не нужно сильно беспокоиться о ложных сигналах. Также проще ожидать нескольких событий.

Condvars также могут заменить семафоры, потому что они более универсальны.

0 голосов
/ 19 марта 2010

Я использовал его для отправки синхронизированных сообщений, где был добавлен объект синхронизации.
Синхронизирующий объект состоял из условной переменной со «готовым» логическим значением.
В функции syncMsg :: send () было sync-> wait (), а в функции syncMsg :: handle () - sync-> go ().

Следует использовать разумно из-за возможных тупиков.

...