В общем случае единичная глобальная блокировка менее эффективна (больше споров), но безопаснее (нет риска тупиковой ситуации), если она RLock
(реентерабельная), а не обычная Lock
.
Потенциальные проблемы возникают, когда поток, выполняющийся во время удержания блокировки, пытается получить другую (или такую же) блокировку, например, вызывая другой метод, содержащий вызов acquire
. Если поток, который уже удерживает блокировку, пытается получить ее снова, он будет блокироваться навсегда, если блокировка простая Lock
, но будет проходить гладко, если она немного сложнее RLock
- поэтому последняя называется reentrant , поскольку удерживающая его нить может снова «войти» (получить блокировку). По сути, RLock отслеживает , какой поток удерживает его, и сколько раз поток получает блокировку, в то время как более простая блокировка не хранит такую информацию.
При множественных блокировках проблема взаимоблокировки возникает, когда один поток пытается получить блокировку A, а затем блокировку B, в то время как другой пытается получить сначала блокировку B, а затем блокировку A. Если это произойдет, то рано или поздно вы окажетесь в ситуация, когда первая блокировка удерживает A, вторая - B, и каждый пытается получить блокировку, которую удерживает другой - так что оба блокируются навсегда.
Один из способов предотвращения взаимных блокировок с несколькими блокировками - убедиться, что блокировки всегда получаются в одном и том же порядке, независимо от того, какой поток выполняет получение. Однако, когда каждый экземпляр имеет свою собственную блокировку, это чрезвычайно трудно организовать с какой-либо ясностью и простотой.