1K подписчиков

Программирование на Python. Многозадачность. Потоки. Threading. Взаимодействие потоков. Синхронизация

Доброго времени суток, читатели, зрители моего канала programmer's notes. Не забывайте подписываться и писать свои комментарии к моим статьям и видео.

Приложение 3 (Приложение 1, Приложение 2) к видео

Многозадачность. Взаимодействие между потоками (Thread). Класс Lock

Очень важный вопрос связан с синхронизацией работы потоков. Существует довольно много инструментов в модуле threading. Рассмотрим сегодня один из инструментариев.

Потоки могут взаимодействовать друг с другом, потому как они имеют одни и те же глобальные переменные. Но вопрос заключается в том, что порядок того, как получают активность потоки не определён. Если в одном потоке стоит a = a + 10, а в другом a = a * 3. Ясно, что порядок в котором будет дано управление потоком даст два разных результата. Если мы хотим как-то настроить последовательность выполнения потоков, нам придётся использовать тот или иной механизм.

Сегодня рассмотрим класс Lock из модуля threading. После создания объекта класса Lock можно использовать следующие методы

  • acquire(blocking=True, timeout=-1) — при выполнении со значениями по-умолчанию, ставит блокировку и сразу возвращает управление. Если блокировка установлена, то ждёт, когда произойдёт её снятие в другом потоке. Если указан таймаут, то ждёт разблокирования указанное и время и возвращает управление по истечении указанного срока. Если первый параметр равен False, то метод по возможности устанавливает блокировку и возвращает управление (возвращает True). Если блокировку установить не удалось, то метод возвращает False.
  • release() — снимает блокировку, если она установлена и выбрасывает исключение, если блокировки нет.
  • locked() — возвращает True, если блокировка есть и False, если блокировка не установлена.

Описанный механизм очень удобен, когда некоторый фрагмент кода выполняют несколько потоков. С помощью указанных методов, в частности acquire(), можно обеспечить, чтобы в данный момент времени указанный фрагмент выполнял только один поток. Мы покажем такой вариант использования в одном из следующих текстовых приложений. Ниже представлен несколько более сложный пример.

В программе есть два потока th1 и th2. Они должны один за другим выполнять некоторое действие. В нашем случае это просто print(), но может что-то более сложное, например, какие-то действия с файлом. Как раз acquire() обеспечивает возможность, чтобы действия в данный момент выполнял только один поток. Есть еще поток th3, который периодически проверяет установлена ли блокировка и снимает её, если установлена.

Текст программы см. ниже
Текст программы см. ниже

Ниже представлена работа программы

Поток 1
Поток 2
Поток 1
Поток 2
Поток 1
Поток 2
Поток 1
Поток 2
Поток 1
Поток 2
Конец работы всех потоков

Замечание (важное)
В алгоритме программы есть один "шаткий" момент. Поток th3 проверяет блокировку приблизительно через 1 секунду. Если предполагаемые действия потоков th1 и th2 будут слишком длинными во времени, то механизм не будет работать должным образом. Тогда придётся увеличивать время ожидания. Либо вводить дополнительный флаг, который будет указывать, что действие уже произведено. В одном из следующих текстовых статей я вернусь к такому механизму.

Хорошего программирования. Оставляйте свои комментарии, не забывайте про лайки и подписывайтесь на мой канал programmer's notes.

Если с вашей программой легко работать, может быть вы ещё не всему научились в программировании ;)
Если с вашей программой легко работать, может быть вы ещё не всему научились в программировании ;)