Добавление замков в класс по составу

Я пишу потокобезопасный класс на C ++. Все его общедоступные методы используют блокировки (нерекурсивные спин-блокировки). Приватные методы не блокируются. Итак, все должно быть в порядке: пользователь вызывает общедоступный метод, он блокирует объект, а затем выполняет работу через частные методы. Но у меня возникла тупиковая блокировка, когда общедоступный метод вызывает другой общедоступный метод. Я читал, что рекурсивные мьютексы - это плохо, потому что их сложно отлаживать. Поэтому я использую метод C stdio: открытый метод Foo () только блокирует объект и вызывает Foo_nolock () для выполнения всей работы. Но мне не нравятся эти методы _nolock (). Думаю, это дублирует мой код. Итак, у меня есть идея: я сделаю класс без блокировок BarNoLock и поточно-ориентированный класс Bar, который имеет только один член: экземпляр BarNoLock. И все методы Bar будут блокировать только этот член и вызывать его методы. Это хорошая идея или, может быть, есть лучшие шаблоны / практики? Спасибо. Обновление: я знаю про pimpl и бридж. Я спрашиваю о шаблонах многопоточности, а не о ООП.


person f0b0s    schedule 16.08.2010    source источник


Ответы (2)


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

Рекурсивная блокировка (мьютекс) против нерекурсивной блокировки (мьютекс)

Но я не думаю, что это обязательно ваша проблема, потому что критические разделы Win32 поддерживают несколько записей из одного потока без блокировки. Из doc:

When a thread owns a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. To release its ownership, the thread must call LeaveCriticalSection one time for each time that it entered the critical section. There is no guarantee about the order in which waiting threads will acquire ownership of the critical section

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

person bshields    schedule 16.08.2010
comment
ну вы правы и CS рекурсивен. Мое приложение не блокируется на практике, только теоретически. Но я не хочу использовать рекурсивные блокировки. - person f0b0s; 16.08.2010
comment
Что ж, если вы категорически против рекурсивных блокировок, то то, что вы предлагаете, звучит разумно. Единственное, что я бы сказал, это то, что вы столкнулись с проблемами с исходным дизайном только тогда, когда одна публичная функция вашего объекта вызывала другую. Очевидным обходным решением было бы просто не делать этого, и всякий раз, когда у вас есть общие функции, которые необходимо совместно использовать, выделите их в частный неблокирующий метод. Затем каждый общедоступный метод снимает блокировку и выполняет свою работу, а используемые частные методы не снимают блокировки. - person bshields; 16.08.2010
comment
bshields: итак, вы советуете мне поместить все функции общедоступных методов в закрытые методы без блокировки: Foo () - ›Foo_nolock ()? Это был оригинальный дизайн - person f0b0s; 16.08.2010
comment
и Foo_nolock () и т. д. должны быть в порядке - я не понимаю, как вы получаете дублирование кода, о котором беспокоились. - person nos; 16.08.2010
comment
но как вы думаете, сочетание всех методов - хорошая идея? Может быть лучше разложение (это был первоначальный вопрос)? Так что будет 2 версии класса. - person f0b0s; 16.08.2010
comment
Не все общедоступные методы, а только те, которые вы хотите вызвать из других общедоступных методов, поскольку именно там возникает проблема. Я понимаю, что это ваш оригинальный дизайн, я думаю, что он работает лучше, если предположить, что большинство ваших общедоступных методов не вызывают другие общедоступные методы. Таким образом, вам не нужно иметь версию _noLock для каждого метода. Используя подход двух классов, у вас будет больше шаблонов, потому что объект-оболочка блокировки должен иметь соответствующие подписи для всех общедоступных методов и передавать их объекту блокировки. - person bshields; 16.08.2010
comment
Как вариант _nolock дублирует код, а Bar / BarNoLock - нет? Просто методы _nolock теперь есть в BarNoLock. Я думаю, что я бы пошел по маршруту Bar / BarNoLock, используя идиому pimpl. Тогда BarNoLock станет частным внутренним классом Bar. - person Frank Osterfeld; 16.08.2010
comment
да, об этом я говорил. - person f0b0s; 16.08.2010

Похоже, вы заново изобрели Шаблон моста. Звучит в полном порядке.

person doron    schedule 16.08.2010
comment
Вот о чем я говорил - мост или pimpl, да. Я спрашивал о многопоточности идиомах, а не о ООП. - person f0b0s; 16.08.2010