Я хотел бы спросить, можно ли использовать 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 мьютексом. Поэтому мне любопытно узнать, можно ли использовать вышеуказанную программу или она в корне ошибочна.
Хорошо.
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 просыпается от ожидания, он должен повторно получить мьютекс, с которым он связан, если разные потоки использовали его с разными мьютексами, он мог бы повторно заблокировать неправильный мьютекс в неправильном потоке, вызывая хаос. Поток проснется, думая, что он снова заблокировал мьютекс, с которым он ждал, но на самом деле он заблокирован другим мьютексом, возможно, тем, к которому он не имеет никакого отношения, и поэтому никогда не сможет разблокировать. Тупик и / или неопределенное поведение и львы, и тигры, и медведи, о боже.
В вашем случае нет одновременных ожиданий, поэтому нет проблем. Если бы у вас было более одного потока, ожидающего, вам нужно было бы убедиться, что оба потока используют один и тот же мьютекс для любого данного ожидания … что было бы сложно, так как вам потребовалась бы дополнительная синхронизация, поэтому было бы проще просто использовать два кондвара.
Других решений пока нет …