Можно ли использовать 1 условную переменную, связанную с 2 мьютексами (отдельно)

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

В основном у меня есть thread1 и thread2. Поток 1 мог ждать 2 вида обновления данных, поэтому он получил 2 мьютекса, по одному для каждого. (Я знаю, что мог бы использовать один мьютекс для всех этих данных, но это не главное в этом вопросе, верно?) И я, конечно, не хочу, чтобы он ожидал на data1, пока data2 уже доступен, поэтому он получил только 1 условную переменную , И поток 2 предоставит как данные1, так и данные2. Проблема в том, что в thread2 я не знаю, ожидает ли thread1 данных1 или data2 или вообще не ждет.

Псевдокод будет выглядеть так:

global data:
cond_var
mutex1
data1
mutex2
data2

thread1:
while true
lock1.lock(mutex1)
if data1 is not available
cond_var.wait(lock1)
if data1 is available
process data1
lock1.unlock()

lock2.lock(mutex2)
if data2 is not available
cond_var.wait(lock2)
if data2 is available
process data2
lock2.unlock()

thread2:
func1:
lock1.lock(mutex1)
update data1
cond_var.notify_all()
lock1.unlock()

func2:
lock2.lock(mutex2)
update data2
cond_var.notify_all()
lock2.unlock()

Внешний мир вызовет func1 или func2 для обновления данных.
Когда вызывается func1 или func2, он сигнализирует cond_var, находится ли он в lock1 или lock2. Ожидание cond_var не окружено while, поэтому, если cond_var активируется в lock1, но data2 доступна, thread1 продолжит обработку data2.

Фактическая реализация осуществляется с помощью boost :: thread, и поскольку платформой для моего теста является Linux, boost :: thread должен быть реализован с помощью pthread.

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

3

Решение

Хорошо.

Boost.Thread реализует библиотеку потоков C ++ 11 (у нее были некоторые различия до v1.50, но теперь она очень близка), и эта библиотека потоков концептуально близка к модели Pthread, но использует другой API, поэтому мы можем взглянуть на эти спецификации для ответа.

Правило в стандарте C ++ 11:

Требуется: lock.owns_lock() является true а также lock.mutex() заблокирован вызывающим потоком, и либо
— никакой другой темы на этом не ожидается condition_variable объект или
lock.mutex() возвращает одинаковое значение для каждого из lock аргументы предоставлены всем одновременно ожидающим (via wait, wait_for, или же wait_until) потоки.

В вашем случае мьютекс правильно заблокирован, и только один поток ожидает на condvar, поэтому условие выполнено.

Правило в POSIX эквивалентно, но сформулировано иначе:

Эффект использования более одного мьютекса для одновременного pthread_cond_timedwait() или же pthread_cond_wait() операции с одной и той же условной переменной не определены; то есть переменная условия становится связанной с уникальным мьютексом, когда поток ожидает переменную условия, и это (динамическое) связывание заканчивается, когда возвращается ожидание.

Опять же, у вас нет одновременных операций ожидания.

В общем, можно использовать condvar с разными мьютексами, если вы не ждете, используя разные мьютексы. в то же время. При ожидании condvar вы связываете его с мьютексом и предикатом («условием»), как описывает POSIX, condvar и mutex «связаны» вместе, и любой condvar не должен быть привязан более чем к одному мьютексу за раз , Причина в том, что, когда condvar просыпается от ожидания, он должен повторно получить мьютекс, с которым он связан, если разные потоки использовали его с разными мьютексами, он мог бы повторно заблокировать неправильный мьютекс в неправильном потоке, вызывая хаос. Поток проснется, думая, что он снова заблокировал мьютекс, с которым он ждал, но на самом деле он заблокирован другим мьютексом, возможно, тем, к которому он не имеет никакого отношения, и поэтому никогда не сможет разблокировать. Тупик и / или неопределенное поведение и львы, и тигры, и медведи, о боже.

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

1

Другие решения

Других решений пока нет …