У меня есть две функции, скажем, foo() и bar(), я хочу, чтобы они были взаимоисключающими, то есть полностью блокировали работу foo(), когда работает bar(), или полностью блокировали работу bar(), когда foo() работает для безопасности потоков.
Тем не менее, я могу вызвать bar() внутри foo(), то есть когда foo() вызывает bar() внутри foo(), пусть выполняется bar(), но никакие другие потоки не вызывают bar().
Является ли это возможным? Если да, можете ли вы дать общую идею, пожалуйста?
Пытался использовать один или несколько мьютексов в C, легко сделать две функции взаимоисключающими, но тогда я не могу вызвать bar() внутри foo(), они войдут в тупик.
Я не могу просто разблокировать мьютекс прямо перед вызовом bar() внутри foo(), потому что я не могу гарантировать, что следующий запущенный bar() будет вызван внутри foo().
Я ищу решение, при котором foo будет блокировать bar, а bar будет блокировать foo, если они будут работать в разных потоках. но когда foo вызывает bar внутри своего тела (тот же поток), пусть bar работает.
Спасибо
Моя идея состоит в том, чтобы использовать идентификатор потока в качестве базы и мьютексировать этот идентификатор, поэтому T1 вызывает Foo, устанавливает идентификатор потока и продолжает, T2 вызывает Bar, блокируется (другой идентификатор потока). T1 вызывает Bar , может с того же идентификатора потока. Это очень-очень общая идея.
Показать код для нас
@Gar Если я хорошо понял вашу идею, единственный способ получить доступ к bar() - это вызвать его из foo(), это не очень хорошо:/ и что произойдет, если один поток войдет в foo(), а другой - в bar(), и они оба wan 't для доступа к другой функции, у вас будет тупик.
Вовсе нет, если bar не вызывается (ни foo), любой из них может быть вызван, поскольку сохраненный threadID равен null , как только один из них будет вызван, threadID будет заполнен и остановит вход любого другого потока.
@Gar Хорошо понял твою идею, теперь я понял. Вроде должно работать :)
Я думаю, вам следует пересмотреть то, о чем вы спрашиваете. Для безопасности потоков вас действительно интересует, какие данные будут изменены; вам не нужно/не следует заботиться о том, какие функции выполняются (кроме случаев, когда они изменяют общие данные). Перевернув его, это может сделать вашу задачу более ясной. Кажется вероятным, что рекурсивный мьютекс легко решит вашу проблему, но опять же, это требует тщательного рассмотрения того, что на самом деле охраняется.
Спасибо всем
Самый простой способ сделать это — переместить основную реализацию bar()
и foo()
в отдельные вспомогательные функции bar_unlocked()
и foo_unlocked()
, а затем реализовать bar()
и foo()
как:
type foo(args)
{
type result;
pthread_mutex_lock(&barfoo_lock);
result = foo_unlocked(args);
pthread_mutex_unlock(&barfoo_lock);
return result;
}
type bar(args)
{
type result;
pthread_mutex_lock(&barfoo_lock);
result = bar_unlocked(args);
pthread_mutex_unlock(&barfoo_lock);
return result;
}
Вы можете безопасно вызывать bar_unlocked()
из реализации foo_unlocked()
.
Большое спасибо, это именно то, что мне нужно.
Другой способ сделать это — использовать так называемый «реентерабельный» или «рекурсивный» объект мьютекса/блокировки.
https://en.cppreference.com/w/cpp/thread/recursive_mutex
Он работает точно так же, как обычная блокировка, за исключением того, что вызов lock()
из потока, который уже заблокировал его, будет успешным, а вызов unlock()
нет разблокирует мьютекс до тех пор, пока поток unlock()
не заблокирует блокировку столько раз, сколько он lock()
замок.
Рекурсивные блокировки - это функция, которая появляется во многих различных многопоточных библиотеках, но я не знаю о библиотеке pthreads, поэтому, возможно, ответ @caf будет для вас лучшим выбором.
Рекурсивные мьютексы могут быть созданы в pthreads путем инициализации мьютекса с помощью объекта mutexattr, для которого тип мьютекса установлен на PTHREAD_MUTEX_RECURSIVE
с pthread_mutexattr_settype()
. Однако, как правило, проще анализировать поведение программы, если не полагаться на подобные вещи.
However, I may call bar() inside foo(), this is, when foo() calls bar() inside foo(), let bar() to run, but not any other threads call bar().
Никогда не пытайтесь объяснить код, возможно, включите пример кода.