Appearance
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()
Основные выводы:
Использование
with
предпочтительнее, чем явныеtry-finally
сacquire/release
, потому что:- Код становится короче и читабельнее
- Меньше вероятность ошибок
- Автоматическое освобождение блокировки даже при возникновении исключений
Комбинировать
with
иtry-except
имеет смысл, когда нужно:- Обработать конкретные исключения
- Выполнить специфическую логику при ошибках
- Логировать ошибки
Использовать
try-finally
вместе сwith
обычно избыточно, так как:with
уже обеспечивает гарантированное освобождение ресурса- Создает ненужную вложенность
- Усложняет код без добавления функциональности
Если нужна дополнительная логика очистки, лучше использовать отдельный блок
try-except-finally
внутриwith
.
Упражнения
Создайте функцию, которая использует блокировку для защиты доступа к общей переменной и изменяет её значение. Используйте конструкцию
try-finally
сacquire/release
.Напишите программу, которая использует несколько потоков для доступа к общей переменной, защищенной блокировкой, и обрабатывает возможные исключения внутри блока
with
.Модифицируйте функцию из первого упражнения, добавив дополнительные действия до и после критической секции, используя
try-finally
вместе сwith
.Создайте функцию, которая использует блокировку для защиты доступа к файлу, обрабатывает возможные исключения при чтении/записи файла и выполняет завершающие действия, используя конструкцию
try-except-finally
внутри блокаwith
.Напишите программу, которая создает несколько потоков, каждый из которых захватывает одну и ту же блокировку, выполняет критическую секцию с возможной ошибкой и обрабатывает исключение, выводя сообщения о захвате и освобождении блокировки.
Напишите программу, которая используется для блокировки ресурса в течение 5 секунд. Используйте with-блокировку.
Напишите программу, которая создает 10 потоков, каждый из которых блокирует ресурс в течение 1 секунды. Используйте try-finally с acquire/release.
Напишите программу, которая создает 5 потоков, каждый из которых блокирует ресурс в течение 2 секунд. Используйте try-finally вместе с with.
Напишите программу, которая блокирует ресурс в течение 10 секунд и затем разблокирует его. Используйте with-блокировку.
Напишите программу, которая создает 3 потока, каждый из которых блокирует ресурс в течение 3 секунд. Используйте отдельный блок try-except-finally внутри with (это не рекомендовано, но для интереса).
Критическая секция с суммированием: Напишите функцию, которая суммирует элементы списка, используя Lock для обеспечения потокобезопасности.
Запись в лог-файл: Напишите функцию, которая записывает сообщения в лог-файл, используя Lock для предотвращения конкурсных доступов.
Счетчик потоков: Напишите класс, который подсчитывает количество активных потоков, используя Lock для обеспечения потокобезопасности.
Безопасная очередь: Реализуйте безопасную очередь с помощью Lock, чтобы предотвратить конкурсные доступы.
Критическая секция с временной задержкой: Напишите функцию, которая выполняет критическую секцию с временной задержкой, используя Lock для обеспечения потокобезопасности.
Напишите программу, которая использует Lock для синхронизации доступа к общему ресурсу. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.
Напишите программу, которая использует with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.
Напишите программу, которая использует try-finally вместе с with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.
Напишите программу, которая использует try-except-finally вместе с with для блокировки и разблокировки ресурса. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно.
Напишите программу, которая использует Lock для синхронизации доступа к общему ресурсу. Создайте несколько потоков, которые пытаются доступиться к ресурсу одновременно, и используйте time.sleep() для имитации задержки в доступе к ресурсу.