2014-10-30 6 views
2

У меня возникают проблемы с блокировкой в ​​моей программе. Поэтому я читал о блокировках, но проблема в большей части информации не соответствует или не определена платформой. В Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex) наиболее общепринятый ответ говорит:Рекурсивные и нерекурсивные блокировки (Mutex)

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

В комментариях люди говорят, что это неверно, и об этом нет. Итак ...

1) Если я заблокирую нерекурсивный мьютекс в потоке A. Может ли резьба B разблокировать ее, не захватывая замок?

2) Если в блокировке был сделан нерекурсивный мьютекс потоком A и потоком B, чтобы получить блокировку, поток B будет ждать, пока блокировка не будет отпущена, чтобы получить блокировку или выкинет исключение ? Как насчет этого случая в рекурсивном мьютексе? (Также обсуждались в других вопросах, где нельзя было сделать достойное заключение)

3) При использовании рекурсивных замков при завершении процесса необходимо ли освободить все рекурсивные блокировки? (В зависимости от того, заканчивается ли процесс)

4) С какими проблемами я сталкиваюсь при использовании комбинации рекурсивных и нерекурсивных замков с осторожностью?

PS: Использование только оконной платформы и std::thread.

ответ

6

Я думаю, вам помогут без помех, прочитав wiki на Reentrant Mutexes. Я согласен с комментариями в этой другой теме; Принятый ответ неверен или, по крайней мере, объясняет его точку очень, очень плохо.

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

В чем разница между рекурсивным/Reenrant и регулярным Mutex? A Рекурсивный мьютекс может быть заблокирован несколько раз по той же теме. Процитировать wiki:

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

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

Действительно, это только причине использования рекурсивного мьютекса; В большинстве других ситуаций, когда вы получаете один и тот же поток, пытающийся получить один и тот же замок, не освобождая его, возможно, будет реорганизован, чтобы правильно получить/освободить блокировку без необходимости рекурсивного мьютекса. И сделать это будет намного безопаснее; Рекурсивная функция, естественно, собирается пузыриться и освобождать каждую блокировку рекурсивного мьютекса, предполагая RAII, где, как и в других ситуациях, вы можете не достаточно освобождать мьютекс и все же заканчиваться тупиком.

Таким образом, чтобы ответить на ваши конкретные вопросы:

  1. Нет, если в вашей системе мьютекса специально не позволяет это
  2. Да, в обоих случаях, как правило, хотя опять же это реализация мьютекс конкретным в отношении блокирования/метания. Почти каждая система, которую я когда-либо использовал, просто блокировал (и именно поэтому нерекурсивные взаимные блокировки мьютексов, если один и тот же поток блокируется дважды без свободного)
  3. Да, обычно, хотя обычно они будут освобождены при условии правильного RAII, и процесс прекратит изящество , Процессы, заканчивающиеся не изящно, удерживая блокировки, могут быть немного crapshoot.
  4. См. Мои объяснения выше. В частности, обратить внимание на следующие из вики:

Обратите внимание, что рекурсивный замок называется освобождено, если и только если число раз он получен соответствует числу раз он был освобожден по собственному потоку.

+0

_The принял ответ неверен, или, по крайней мере, объясняет свою точку очень, очень poorly._ It кажется, ваш принятый в настоящее время ответ мог использовать охранник. – bvj

0

Действительно, вам нужно написать простую программу для проверки этих случаев.

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

  2. Рассмотрим код:

    void criticalSection(){ 
        pthread_mutex_lock(&mutex) 
        //dostuff 
        pthread_mutex_unlock(&mutex) 
    } 
    

    Если есть две нити, нити А и В, и поток А входит в функцию первой, она получает блокировку и делает вещи. Если поток B входит, пока поток A все еще находится в функции, он будет заблокирован. Когда резьба А исполняется

    pthread_mutex_unlock(&mutex) 
    

    Резьба B теперь будет «проснута» и начнет делать вещи.

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

  4. Предположим, вы смотрите на многопоточное приложение без условий гонки.:-)

+2

Практически всегда при многопоточности лучше писать правильный код, не основанный на поведении, а на документации. Итак, «вы должны написать простую программу для проверки этих случаев». ИМХО - плохой совет, поскольку это легко может ввести в заблуждение. – Slava

+0

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

+0

«это просто вопрос, может ли другой поток разблокировать заблокированный мьютекс», в случае блокировки спина он может «работать» в тестовой программе. Это не значит, что его следует использовать таким образом. – Slava

1

Вы имеете в виду POSIX Мьютексы обсуждения, но для Windows не поддерживает POSIX в любом случае, и вы используете C++ стандарт поточных примитивов, которые могут отличаться в деталях.

Так лучше проверить стандартную документацию библиотеки первой, например, C++ стандарт unlock явно заявляет:

Требуется: Вызывающий поток должен владеть мьютекс.

1

Ниже со страницы pthread_mutex_lock людей Linux,

Переменные типа pthread_mutex_t также может быть инициализирован статический, используя константу PTHREAD_MUTEX_INITIALIZER (для быстрых мьютексов), THREAD_RECURSIVE_MUTEX_INITIALIZER_NP (для рекурсивных мьютексов), и PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP (для мьютексов проверки ошибок).

error checking'' and Рекурсивные «мьютексы», pthread_mutex_unlock фактически проверяет во время выполнения, что мьютекс заблокирован на входе, и что он был заблокирован тем же потоком, который теперь вызывает pthread_mutex_unlock. Если эти условия не выполняются, возвращается код ошибки, и мьютекс остается неизменным. «Быстрые» мьютексы не выполняют таких проверок, что позволяет блокировать блокировку мьютекса с помощью потока, отличного от его владельца. Это не переносное поведение, на которое нельзя положиться.

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

Смежные вопросы