Время прочтения: 4 мин.

Любой аудитор, устав обрабатывать кучу файлов руками, начинает мечтать о программе, которая будет делать анализ документов за него. Осуществить эти мечты нам поможет язык Python! Python очень простой язык программирования, а его возможности крайне большие, словно тихий океан, только еще больше!

Но если в океане водится разная живность и другие любители поплавать, то в Python существует куча модулей (от тех, что работают с документами Excel, до тех, что строят нейросети!)

Итак, предположим, нашего хладнокровного друга вы уже приручили. Есть только маленькая проблема. Порой документов для обработки так много, что программа работает очень медленно.

Надо ускориться! Выход есть — многопоточность! То есть выполнение нескольких наших инструкций одновременно. Организовать многопоточное приложение без внешних библиотек в python можно при помощи следующих модулей: threading — для управления потоками, queue —  для организации очередей, multiprocessing  — для управления процессами.

На мой взгляд, threading наиболее простой среди них, поэтому сегодня поговорим о нем.

Например, нам нужно сделать так, чтобы все страницы документа открывались и обрабатывались одновременно. Это должно ускорить работу нашего приложения. Для этого мы пишем простую функцию по открытию страницы, а при помощи модуля — threading-  создаем потоки. В итоге наша функция для обработки одной страницы будет вызываться вот таким простым кодом:

variable = Thread(target=function_name, args=(arg1, arg2,))

variable.Start()

variable.Join()

Именно таким способом мы можем обрабатывать все наши страницы одновременно. Хочется отметить, что последняя запятая- не ошибка)  Получается, что Start() запускает нашу функцию, а функция Join() останавливает поток после выполнения своих задач. Как в большом офисном помещении – закрываем окна, чтобы не просквозило. Функция остановки потока нужна для закрытия открытых файлов и освобождения занятых ресурсов. Плюс — это надежнее – закрыть поток самому в нужный момент, чем он закроется снаружи и неизвестно когда и кем. Уменьшается риск воздействия  случайных факторов.

Но есть одна маленькая проблема. Например, мы запустили программу, и поняли, что документ обрабатывается не тот, нужен другой. Или же рабочий день заканчивается, а программа еще работает. Проблема в том, что, обычно, Python-приложение не завершается до тех пор, пока работает хотя бы один поток. То есть, оно будет продолжать обработку, пока запущена хотя бы одна часть программы. И хотя визуально все уже закрыто, приложение продолжает работать в недрах всей системы. Это нам не всегда удобно.  И тут выступает темная сторона-параметр потока –daemon-. Если он приобретает значение  -True-, то поток завершается вместе с приложением, оно не зависает на просторах ПК.

Итак, предположим, мы все сделали правильно, потоки прописали, ждем быстрого результата, но что-то идет не так. Время на обработку информации уходит еще больше…

Нельзя просто взять и воспользоваться всеми преимуществами многопоточности в Python! Преграда — «огромная дверь».  Даже так — глобальный шлюз (Global Interpreter Lock, он же GIL), который ограничивает многопоточность на уровне интерпретатора.

Задача шлюза — пропускать потоки строго по одному, чтоб они не летали наперегонки, как печально известные стритрейсеры, и не создавали угрозу работе интерпретатора.

Да, конечно, есть способы обхода этого ограничения, но они уже решаются куда более сложными инструментами.

В итоге, из своей практики, могу сказать следующее: многопоточность – это наш путь, но в тех случаях:

  1. Если она  используется для длительных и несвязанных друг с другом операций ввода-вывода;
  2. Если вычисления занимают более миллисекунды и вы хотите сэкономить время за счёт их параллельного выполнения.

Во всех остальных случаях GIL не даст нам ускорить работу, а лишь ее затормозит.

Как раз с обработкой большого количества Excel файлов многопоточность нам поможет.

Итог следующий – потоки в Python –e очень даже простая и интересная вещь, но с подводными камнями!