Python: Работа с потоками

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

Главный поток у нас всегда один, вторых потоков может быть сколько угодно, а третьих (вычисляющих) — только один запущенный.

Для наглядности реализуем всё сначала без потоков. Создадим класс MainTask со следующими методами:

  • runMainTask(self, sHash, sFolder)
  • searchFile(self, s_hash, s_folder)
  • runHashTask(self, s_folder, s_hash)
  • runMainTask — в общем то почти ничего не делает ;-). Запускает метод searchFile с соответствующими параметрами.

searchFile — проверяет БД на наличие искомого хеша, если записи нашлись — выводит их и завершается. Если записи не найдены — вызывает метод runHashTask, затем опять проверяет БД.

runHashTask — рекурсивно (т.е. включая подкаталоги) обходит переданный через параметры путь и хеширует файлы. Если во время обхода встречается искомый хеш — добавляет результат вычислений (все вычисленные хеши для файлов) в БД и завершается. Если хеш не был найден — делает то же самое (обновляет БД и завершает работу) ;-).

Для хранения результатов вычисления была выбрана база данных, что-бы не использовать какой-либо сервер был выбран SQLite (СУБД работает без сервера на основе файлов).

Для нашего приложения нужна всего одна табличка с двумя полями — «path» и «file_hash». Саму таблицу назовём «hashes». Поле «path» будет содержать полный путь к файлу включая его имя, а «file_hash» — хеш этого файла (неожиданный поворот событий? ;-) ).

Так же сразу я решил описать функцию add_hashes. Получает в качестве параметра словарь вида [«file0″:»hash0», «file1″:»hash1», …] и пытается добавить его в БД.

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

Основной модуль (task_1.py):

Теперь попробуем сделать всё то же самое, но используя потоки. Модуль для работы с БД менять не надо, все изменения коснуться только основного модуля.

runMainTask теперь должен вызывать исполнение метода searchFile в отдельном потоке (так же, как и searchFile должен вызывать runHashTask). Перед вызовом третьего потока мы используем блокировку (threading.Lock()), что не позволяет одновременно нескольким вторым потокам запустить третий.

Способ использования блокировок:

Можно так же использовать один и тот же экземпляр замка в разных местах, если надо чтобы эти участки кода не выполнялись одновременно.

Порождение дочернего потока происходит с помощью класса Thread в модуле threading.

Описание использованных параметров класса Thread:

target — указывает на имя функции (в данном случае метод класса), которую стоит выполнять в новом потоке

name — имя порождаемого потока

args — список аргументов, которые мы передаём в функцию, указанную в параметре target

Поток создаётся так:

Запускается на исполнение поток путём выозова метода start:

Метод join() ожидает завершения потока.

Ну, вроде бы теперь должно быть всё понятно. Посмотрим что у нас должно получить в итоге:

Вывод потоков может быть перемешан, но это сделано для наглядности.

Стоит так же отметить, что преобразование пути в кодировку utf8 вызвано проблемами с SQLAlchemy, как то он странно обрабатывает кодировку cp1251.

Если кто-то не знает как получить md5-хеш файла, вот пример:

Понравилась статья? Поделиться с друзьями:
Комментариев: 2
  1. Аноним

    Блин, чувак, долго не мог понять, как эти гребаные замки использовать, поглядел листинг- а оно проще пареной репы :)

    1. lizz

      Кстати, скоро выложу вариант по-круче ;). Задачку делал для универа, как оказалось, не совсем то, что надо %).

Добавить комментарий

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