Баннер мобильный (1) Пройти тест

Взаимная блокировка

Глоссарий

26 февраля 2024

Поделиться

Скопировано

Содержание

    Взаимная блокировка — это явление, когда несколько процессов пытаются обратиться к одним и тем же данным и в итоге блокируют друг другу доступ к ним. Такое бывает в программировании и в работе с базами данных.

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

    Также взаимную блокировку называют дедлоком (от английского deadlock — тупик). 

    Как это работает

    Когда процесс или запрос обращается к каким-то данным, он блокирует их. Это значит, что никакой другой процесс не может в них вмешаться. Блокировка процессов нужна, чтобы избежать потерь данных — например, если один процесс решает скопировать информацию, в то время как другой ее изменяет. Благодаря блокированию программа становится надежнее и таких случаев не происходит.

    Например, процесс A обращается к ячейке памяти slot, чтобы переписать ее содержимое. Он блокирует эту ячейку от других процессов. Если процесс B захочет обратиться к ячейке slot, ему придется ждать, пока процесс A закончит и освободит ее.

    Но если несколько процессов работают с одними и теми же данными, может возникнуть взаимная блокировка. Суть примерно такая:

    1. процесс A занимает ячейку slot1;
    2. процесс B в это же время занимает ячейку slot2;
    3. процесс A хочет также захватить ячейку slot2, в то время как процесс B собирается работать со slot1;
    4. ни один из них не может перейти к работе с новой ячейкой, потому что ее заблокировал другой процесс;
    5. оба процесса простаивают.
    Пример взаимной блокировки
    Пример взаимной блокировки в реальной жизни: два человека ждут, пока другой освободит им вещи. Только живые люди могут договориться, а процессы — не всегда. Источник

    Такое поведение может привести к тому, что вся программа будет работать некорректно. А еще во взаимной блокировке может участвовать больше двух процессов одновременно.

    В каких ситуациях возникает взаимная блокировка

    Чаще всего ситуация взаимоблокировки встречается при работе с базами данных, а также с многозадачным ПО, которое одновременно запускает несколько процессов. Важное условие — процессы должны работать именно одновременно. Ведь если один успеет чуть раньше другого, он сможет захватить оба ресурса и тупиковой ситуации не возникнет.

    Порой взаимные блокировки сложно находить — такие ошибки могут не воспроизводиться, если, например, при очередном тестировании какой-то процесс запустится раньше и успеет захватить все нужные ему данные. Приходится выстраивать дерево или граф, где показано, какие процессы к каким данным могут обращаться. 

    Некоторые инструменты разработчиков и языки программирования умеют находить взаимные блокировки автоматически. Например, Java Virtual Machine создает дампы, где описывает, какие ресурсы заняты, а какие свободны. На основе дампов JVM обнаруживает взаимоблокировки и информирует о них.

    При чем тут обедающие философы

    Существует логическая задача, которая хорошо отражает суть взаимной блокировки. Это задача об обедающих философах. Она звучит так:

    Пять философов обедают за круглым столом. Каждый из них в определенный момент времени может или есть, или размышлять. Чтобы есть, философу нужны две вилки. А вилок пять, и они лежат по кругу между тарелками. Философы берут их по очереди: сначала одну, потом другую. Задача — написать для философов модель поведения, при которой они будут чередовать прием пищи с размышлениями так, чтобы никто не остался без приборов.

    задача обедающих философов
    Визуально задача обедающих философов выглядит так. В некоторых примерах вместо вилок говорят о палочках для еды. Источник

    Фактически философы в этой задаче — это процессы, а вилки — данные, к которым они обращаются. Может возникнуть ситуация, когда философ взял одну вилку, а вторую не смог: ее забрал другой. А другой в свою очередь не может ее вернуть, потому что ждет, когда вилку освободит третий. И так по кругу — все пятеро с одной вилкой.

    Решений у «обедающих философов» несколько:

    • ввести «официанта», который будет следить за использованием вилок и запрещать философам брать приборы, если это может привести к блокировке;
    • добавить мониторинг, чтобы философы сами проверяли, не ест ли кто-то из их соседей, — и ждали, пока сосед закончит;
    • назначить вилкам порядковые номера и обязать философов сначала брать вилки с меньшим номером.

    Эти же методы можно использовать и для решения проблемы реальной взаимной блокировки.

    Как избежать взаимной блокировки

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

    Игнорирование блокировок. Это наиболее простое, но опасное решение: не блокировать ресурсы, когда с ними работает какой-то процесс. В таком случае взаимоблокировок не возникает и программа работает быстрее, потому что процессы могут обращаться к одним и тем же данным одновременно и не ждать. Но без блокировок нельзя обеспечить сохранность данных, поэтому игнорирование применять не рекомендуется.

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

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

    Диспетчеризация. Вариант, который реализован в некоторых программах и системах управления базами данных. Например, СУБД Oracle умеет обнаруживать взаимные блокировки и «выключать» их, освобождая данные и процессы. В таких случаях блокировка может возникнуть, но программа заметит ее и ликвидирует раньше, чем та нанесет вред. Правда, тут может возникнуть проблема динамической взаимоблокировки — когда взаимная блокировка появляется, сбрасывается, тут же появляется снова, и цикл повторяется.

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

    Считается, что лучший способ избежать взаимных блокировок — с самого начала проектировать систему так, чтобы подобной ситуации не могло возникнуть. А если это невозможно, создать механизм выхода из взаимоблокировок, который не «ломал» бы программу.

    Кому нужно знать о взаимных блокировках

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

    Поделиться

    Скопировано

    0 комментариев

    Комментарии