Что происходит с потоком, который проснулся pthread_cond_signal (), но проиграл мьютекс - PullRequest
2 голосов
/ 29 июня 2011

Относительно этого: Как использовать переменную условия

Скажем, у нас есть количество потоков пользователей, которые выполняют такой код (скопировано со страницы, на которую есть ссылки):

while (TRUE) {
    s = pthread_mutex_lock(&mtx);
    while (avail == 0) {   /* Wait for something to consume */
       s = pthread_cond_wait(&cond, &mtx);
    }
    while (avail > 0) {   /* Consume all available units */ 
        avail--;
    }
    s = pthread_mutex_unlock(&mtx);
}

Я предполагаю, что сценарий здесь такой: основной поток вызывает pthread_cond_signal (), чтобы указать потокам потребителя выполнить некоторую работу.

Как я понимаю - последующие потоки вызывают pthread_mutex_lock () и затем pthread_cond_wait () (который атомарно разблокирует мьютекс). К настоящему времени ни один из потоков-потребителей не претендует на мьютекс, все они ждут pthread_cond_wait ().

Когда основной поток вызывает pthread_cond_signal () после manpage , по крайней мере один поток пробуждается. Когда любой из них возвращается из pthread_cond_wait (), он автоматически запрашивает мьютекс.

Итак, мой вопрос: что теперь происходит с предоставленным примером кода? А именно, что теперь делает поток, проигравший конкурс на мьютекс?

(AFAICT поток, который выиграл мьютекс, должен запустить остальную часть кода и освободить мьютекс. Потерянный должен быть застрял в ожидании мьютекса - где-то в 1-м вложенном while цикл - пока победитель удерживает его и после его освобождения, начните блокировку функции pthread_cond_wait (), так как while (avail == 0) к тому времени будет удовлетворено. Я прав?)

Ответы [ 4 ]

6 голосов
/ 29 июня 2011

Обратите внимание, что pthread_cond_signal() обычно предназначен для пробуждения только одного ожидающего потока (это все, что он гарантирует).Но это может разбудить больше «случайно».Цикл while (avail > 0) выполняет две функции:

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

Это также предотвращает состояние гонки, когда рабочая единица могла быть помещена в очередь послеwhile (avail > 0) завершен, но до того, как рабочий поток снова дождался выполнения условия - но эта гонка также обрабатывается тестом if непосредственно перед вызовом pthread_cond_wait().

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

Итак, последовательность событий, которая происходит, когда pthread_cond_signal() вызывается так:

  • система разбудит один или несколько потоков, ожидающих при условии
  • всех потоков, которыепри пробуждении будет пытаться получить мьютекс - только один из них может получить его в любой конкретный момент, так как это цель мьютекса
  • , который поток затем продолжит, выполняя работу в цикле while (avail > 0), затем освободит мьютекс
  • , и в этот момент один из других потоков, которые были предварительно разбужены, получит мьютекс и будет работать в том же цикле, а затем освободит мьютекс.Как правило, больше не будет доступных рабочих единиц (поскольку первый поток использовал бы их все), но если бы другой поток добавил дополнительную единицу (или больше), то этот поток обработал бы эту работу
  • следующий поток получит мьютекс и выполнит тот же набор логики
2 голосов
/ 29 июня 2011

pthread_cond_wait() должен получить заданный мьютекс после того, как подал сигнал / проснулся.Если другой поток выигрывает эту гонку, функция блокируется до освобождения мьютекса.Таким образом, с точки зрения приложения он не возвращается, пока текущий поток не удерживает мьютекс.Ожидание всегда выполняется в цикле (while (avail == 0) { ... выше), чтобы убедиться, что условие application , которое мы ждем, все еще остается в силе (буфер не пустой, доступно больше работы и т. Д.)

Надеюсь, это поможет.

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

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

0 голосов
/ 29 июля 2016

Когда любой из них возвращается из pthread_cond_wait (), он автоматически запрашивает мьютекс.

Ах, но это не так. Не «автоматически», то есть в зависимости от того, что означает «автоматически». Вы можете быть смущены "атомарной" семантикой pthread_cond_wait; но эта семантика воспроизводится на стороне входа: поток каким-то образом регистрируется для ожидания условия перед отказом мьютекса, так что нет никакого окна, в течение которого у потока больше нет мьютекса, и он еще не ожидает на переменную.

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

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

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

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

...