Skip to content

Lock - блокировка

Использование только try-finally с acquire/release

Методы acquire и release используются для явного захвата и освобождения блокировки. Использование конструкции try-finally гарантирует, что блокировка будет освобождена, даже если внутри критической секции произойдет исключение.

python
from threading import Lock

def critical_section():
    lock.acquire()
    try:
        # Код критической секции
        print("Блокировка захвачена и выполняется код.")
    finally:
        lock.release()
        print("Блокировка освобождена.")
        
lock = Lock()
critical_section()

Особенности

  • Явное управление блокировкой.
  • Требуется больше кода для обработки исключений.

Использование with

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

python
from threading import Lock

def critical_section():
    with lock:
        print("Critical section is entered")  # Ваш код для критической секции
        
lock = Lock()
critical_section()

Особенности

  • Упрощает код, делая его более читабельным и менее ошибочным.
  • Автоматически освобождает блокировку после выполнения блока кода.

Использование with и try-except

Использование контекстного менеджера with значительно упрощает управление блокировками. В данном случае блокировка будет автоматически захвачена в начале и освобождена в конце блока with. Конструкция try-except позволяет обрабатывать исключения внутри критической секции.

python
from threading import Lock

def critical_section():
    try:
        with lock:
            # Код критической секции
            print("Блокировка захвачена и выполняется код.")
            raise ValueError("Ошибка в критической секции.")
    except ValueError as e:
        print(f"Обработано исключение: {e}")
        
lock = Lock()
critical_section()

Особенности

  • Автоматическое управление блокировкой.
  • Обработка исключений внутри критической секции.

Использовать try-finally вместе с with

Нечеткий случай, когда try-finally используется вместе с with. Это не рекомендовано, но может быть полезно в определенных ситуациях.

Комбинирование try-finally с with может быть полезным, если необходимо выполнить дополнительные действия до или после критической секции, но при этом гарантировать освобождение блокировки.

python
from threading import Lock

def critical_section():
    try:
        print("Дополнительные действия до захвата блокировки.")
        with lock:
            # Код критической секции
            print("Блокировка захвачена и выполняется код.")
    finally:
        print("Дополнительные действия после освобождения блокировки.")
        
lock = Lock()
critical_section()

Особенности

  • Возможность выполнения дополнительных действий до и после критической секции.
  • Гарантия освобождения блокировки.

Использовать отдельный блок try-except-finally внутри with

Использование try-except-finally внутри блока with позволяет более гибко управлять исключениями и выполнять завершающие действия, даже если произойдет ошибка.

python
from threading import Lock

def critical_section():
    with lock:
        try:
            # Код критической секции
            print("Блокировка захвачена и выполняется код.")
            raise ValueError("Ошибка в критической секции.")
        except ValueError as e:
            print(f"Обработано исключение: {e}")
        finally:
            print("Завершающие действия внутри блока with.")
            
lock = Lock()
critical_section()

Особенности

  • Гибкость в обработке исключений и выполнении завершающих действий.
  • Автоматическое управление блокировкой.

Практический пример

Практический пример с несколькими потоками:

python
from threading import Thread, Lock
import time

class SharedResource:
    def __init__(self):
        self.lock = Lock()
        self.value = 0

    def update_value(self, thread_name):
        with self.lock:
            try:
                current = self.value
                # Имитация некоторой работы
                time.sleep(0.1)
                self.value = current + 1
                print(f"{thread_name}: обновлено до {self.value}")
                
                if self.value == 5:
                    raise ValueError("Достигнуто значение 5!")
                    
            except ValueError as e:
                print(f"{thread_name}: поймано исключение - {e}")
                # lock автоматически освободится благодаря with

resource = SharedResource()

def worker(name):
    for _ in range(3):
        resource.update_value(name)

# Создаем и запускаем потоки
threads = [Thread(target=worker, args=(f"Поток-{i}",)) for i in range(3)]

for t in threads:
    t.start()

for t in threads:
    t.join()

Основные выводы:

  1. Использование with предпочтительнее, чем явные try-finally с acquire/release, потому что:

    • Код становится короче и читабельнее
    • Меньше вероятность ошибок
    • Автоматическое освобождение блокировки даже при возникновении исключений
  2. Комбинировать with и try-except имеет смысл, когда нужно:

    • Обработать конкретные исключения
    • Выполнить специфическую логику при ошибках
    • Логировать ошибки
  3. Использовать try-finally вместе с with обычно избыточно, так как:

    • with уже обеспечивает гарантированное освобождение ресурса
    • Создает ненужную вложенность
    • Усложняет код без добавления функциональности
  4. Если нужна дополнительная логика очистки, лучше использовать отдельный блок try-except-finally внутри with.

Упражнения

  1. Создайте функцию, которая использует блокировку для защиты доступа к общей переменной и изменяет её значение. Используйте конструкцию try-finally с acquire/release.

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

  3. Модифицируйте функцию из первого упражнения, добавив дополнительные действия до и после критической секции, используя try-finally вместе с with.

  4. Создайте функцию, которая использует блокировку для защиты доступа к файлу, обрабатывает возможные исключения при чтении/записи файла и выполняет завершающие действия, используя конструкцию try-except-finally внутри блока with.

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

  6. Напишите программу, которая используется для блокировки ресурса в течение 5 секунд. Используйте with-блокировку.

  7. Напишите программу, которая создает 10 потоков, каждый из которых блокирует ресурс в течение 1 секунды. Используйте try-finally с acquire/release.

  8. Напишите программу, которая создает 5 потоков, каждый из которых блокирует ресурс в течение 2 секунд. Используйте try-finally вместе с with.

  9. Напишите программу, которая блокирует ресурс в течение 10 секунд и затем разблокирует его. Используйте with-блокировку.

  10. Напишите программу, которая создает 3 потока, каждый из которых блокирует ресурс в течение 3 секунд. Используйте отдельный блок try-except-finally внутри with (это не рекомендовано, но для интереса).

  11. Критическая секция с суммированием: Напишите функцию, которая суммирует элементы списка, используя Lock для обеспечения потокобезопасности.

  12. Запись в лог-файл: Напишите функцию, которая записывает сообщения в лог-файл, используя Lock для предотвращения конкурсных доступов.

  13. Счетчик потоков: Напишите класс, который подсчитывает количество активных потоков, используя Lock для обеспечения потокобезопасности.

  14. Безопасная очередь: Реализуйте безопасную очередь с помощью Lock, чтобы предотвратить конкурсные доступы.

  15. Критическая секция с временной задержкой: Напишите функцию, которая выполняет критическую секцию с временной задержкой, используя Lock для обеспечения потокобезопасности.

  16. Напишите программу, которая использует Lock для синхронизации доступа к общему ресурсу. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.

  17. Напишите программу, которая использует with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.

  18. Напишите программу, которая использует try-finally вместе с with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.

  19. Напишите программу, которая использует try-except-finally вместе с with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.

  20. Напишите программу, которая использует Lock для синхронизации доступа к общему ресурсу. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно, и используйте time.sleep() для имитации задержки в доступе к ресурсу.

Contacts: teffal@mail.ru