Python: Работа с потоками. Часть 2

Мы уже недавно рассматривали примитивную работу с потоками, а именно, запуск потоков и передача им параметров, использование замков и, по большому счёту, всё. Теперь пришло время изучить другие способы взаимодействия потоков в python’е.

Решим ту же самую задачу, что и в предыдущем посте, но несколько изменив условия. Итак, вот:

  1. Главный поток запрашивает у пользователя md5-сумму какого-либо файла и директорию для поиска файла с такой же суммой. Получив эти параметры первый поток запускает 2й и продолжает свою работу (опять ожидает данных от пользователя).
  2. Запущенный поток проверяет есть ли в БД приложения файл с таким хешом. Если есть — выводит результат, иначе, передаёт задания для 3го потока (путь) и по его завершении (задания, не потока) заново проверяет БД. Выводит результат.
  3. 3й поток хеширует все файлы с помощью md5 по указанному пути и результат записывает в базу данных.

Только на этот раз у нас будет действительно один считающий поток, который не будет завершаться, а будет ждать заданий для вычисления.

Модуль для работы с базой данных не изменился (files.py), поменялся только task.py.

Вот логика работы приложения вкратце:

  1. В конструкторе класса объявляем: очередь заданий taskQueue, замок globalTaskEventLock, событие globalTaskEvent — событие, которое будет оповещать потоки о том, что хеширование какой-либо папки закончено, массив localTaskEvent — в нём будут хранится события, которые отвечают за полное выполнение задания какого-то конкретного «ищущего» потока, и counter — счётчик «ищущих» потоков (за всё время работы приложения).
  2. Метод runMainTask(s_hash, s_folder) — в параметрах передаётся искомый хеш и директория, в которой следует искать (при поиске в БД не учитывается). Запускает исполнение метода searchFile в отдельном потоке и увеличивает счётчик «ищущих» потоков на 1.
  3. searchFile(s_hash, s_folder) — аргументы те же, что и у метода runMainTask. Проверяет наличие в базе данных искомого хеша. Если совпадения найдены — выводит их в консоль и завершает работу, иначе добавляет указанную директорию и имя потока, в котором выполняется (сам метод) в очередь заданий, так же добавляет в массив localTaskEvents пару thName => event, где thName — имя потока, а event — событие, наступление которого означает полное выполнения задания (вся папка прохеширована). Затем ждёт в бесконечном цикле события globalTaskEvents (оповещает о том, что хеши для всех файлов в какой-то папке посчитаны и добавлены в БД). При наступлении события проверяет БД, если совпадения найдены — выводит результат, удаляет своё событие из массива localTaskEvents и завершается, если нет — проверяет было ли вычислено его задание, если да — выводит, что ничего не нашёл, опять удаляет свои данные из localTaskEvent и завершается.
  4. runHashTask() — исполняет код в бесконечном цикле. Получает имя потока и папку из очереди заданий. Проверяет в массиве localTaskEvents наличие события для имени потока, которое он получил. Если его нет — то поток уже завершился и удалил своё событие, тогда пытаемся получить следующее задание. Иначе, рекурсивно проходим по всем подпапкам и хешируем все файлы. После того как в какой-то подпапке файлы прохешированы — результат добавляется в БД и с помощью globalTaskEvent оповещаются об этом все потоки. Когда задание завершено — пытаемся оповестить поток, чьё задание это было. При переходе к каждой новой подпапке проверяем надо ли считать ещё это задание или лучше перейти к следующему.

Вот, в общем то, всё, таков мой план.

Листинг нового класса:

Стоит отметить, что при преобразовании строк в юникод с помощью функции unicode стоит указать кодировку, специфичкескую для Вашей ОС (для windows — cp1251, freebsd — koi8-r).

Вот результат выполнения кода:

Конечно, при желании, можно улучшить алгоритм, но, как пример для изучения потоков в python’е, имхо, сойдёт.

Понравилась статья? Поделиться с друзьями:
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: