Файл: Многопоточное программированиеВ этой главе.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 12.12.2023

Просмотров: 99

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

229 4.9. Дополнительные сведения об использовании потоков
В настоящей главе было показано, что применение однопоточного процесса мо- жет стать препятствием к повышению производительности приложения. Особенно значительно может быть повышена производительность программ, основанных на последовательном выполнении независимых, недетерминированных и не имеющих причинных зависимостей задач, в результате их разбиения на отдельные задачи, выполняемые отдельными потоками. Существенный выигрыш от перехода к мно- гопоточной обработке может быть достигнут не во всех приложениях. Причинами этого могут стать дополнительные издержки, а также тот факт, что сам интерпрета- тор Python представляет собой однопоточное приложение. Тем не менее овладение функциональными возможностями многопоточной организации Python позволяет взять этот инструмент на вооружение, когда это оправдано.
4.9. Дополнительные сведения
об использовании потоков
Прежде чем приступать к повсеместному применению средств поддержки мно- гопоточности, следует провести краткий обзор особенностей такой организации программирования. Вообще говоря, применение нескольких потоков в программе может способствовать ее улучшению. Однако в интерпретаторе Python применяется глобальная блокировка, которая накладывает свои ограничения, поэтому многопо- точная организация является более подходящей для приложений, ограничиваемых пропускной способностью ввода-вывода (при вводе-выводе происходит освобожде- ние глобальной блокировки интерпретатора, что способствует повышению степени распараллеливания), а не приложений, ограничиваемых пропускной способностью процессора. В последнем случае для достижения более высокой степени распаралле- ливания необходимо иметь возможность параллельного выполнения процессов не- сколькими ядрами или процессорами.
Не вдаваясь в дополнительные подробности (поскольку некоторые из соответ- ствующих тем уже рассматривались в главе “Среда выполнения” книги “Core Python
Programming” или “Core Python Language Fundamentals”), перечислим основные альтернативы модулю threading, касающиеся поддержки нескольких потоков или процессов.
4.9.1. Модуль
subprocess
В первую очередь вместо модуля threading можно применить модуль subprocess, когда возникает необходимость запуска новых процессов, либо для выполнения кода, либо для обеспечения обмена данными с другими процессами через стандартные файлы ввода-вывода (stdin, stdout, stderr).
Этот модуль был введен в версии Python 2.4.
4.9.2. Модуль
multiprocessing
Этот модуль, впервые введенный в Python 2.6, позволяет запускать процессы для нескольких ядер или процессоров, но с интерфейсом, весьма напоми- нающим интерфейс модуля threading. Он также поддерживает различные механизмы передачи данных между процессами, применяемыми для вы- полнения совместной работы.
06_ch04.indd 229 22.01.2015 22:00:51


Глава 4

Многопоточное программирование
230
4.9.3. Модуль
concurrent.futures
Это новая высокоуровневая библиотека, которая работает только на уровне заданий. Это означает, что при использовании модуля concurrent.futures исключается необходимость заботиться о синхронизации либо управлять потоками или процессами. Достаточно лишь указать поток или пул процес- са с определенным количеством рабочих потоков, передать задания на вы- полнение и собрать полученные результаты. Этот модуль впервые появился в версии Python 3.2, но перенесен также в версию Python 2.6 и последующие версии. Модуль можно получить по адресу http://code.google.com/p/
pythonfutures.
Рассмотрим вариант сценария bookrank3.py с указанными изменениями. При условии, что все прочее остается таким, как прежде, рассмотрим новые операторы импорта и изменившуюся часть сценария _main():
from concurrent.futures import ThreadPoolExecutor
def _main(): print('At', ctime(), 'on Amazon...')
with ThreadPoolExecutor(3) as executor:
for isbn in ISBNs: executor.submit(_showRanking, isbn) print('all DONE at:', ctime())
Методу concurrent.futures.ThreadPoolExecutor передается параметр, пред- ставляющий собой размер пула потоков, а приложение применяется для определе- ния рангов трех книг. Безусловно, это — приложение, ограничиваемое пропускной способностью ввода-вывода, для которого применение потоков оказывает наиболь- шую пользу. Что касается приложений, ограничиваемых пропускной способно- стью процессора, то вместо указанного метода целесообразно было бы использовать concurrent.futures.ProcessPoolExecutor.
После создания управляющего объекта (действие которого распространяется на потоки или процессы), отвечающего за планирование заданий и сбор результатов, можно вызвать его метод submit() для выполнения намеченной ранее задачи по- рождения потока.
После полного переноса в версию Python 3 путем замены оператора формати- рования строки методом str.format(), повсеместного введения инструкции with и использования метода map() управляющего объекта появляется возможность полно- стью удалить метод _showRanking() и передать его функции в программу _main().
Заключительная версия сценария bookrank3CF.py приведена в примере 4.13.
Пример 4.13. Применение средств управления заданиями высокого
уровня (
bookrank3CF.py)
В этом участке кода, как и в предыдущих примерах, осуществляется сбор с экрана данных о рангах книг, но на этот раз с помощью модуля concurrent.futures.
1 #!/usr/bin/env python
2 3 from concurrent.futures import ThreadPoolExecutor
4 from re import compile
06_ch04.indd 230 22.01.2015 22:00:51


231 4.9. Дополнительные сведения об использовании потоков
5 from time import ctime
6 from urllib.request import urlopen as uopen
7 8 REGEX = compile(b'#([\d,]+) in Books ')
9 AMZN = 'http://amazon.com/dp/'
10 ISBNs = {
11 '0132269937': 'Core Python Programming',
12 '0132356139': 'Python Web Development with Django',
13 '0137143419': 'Python Fundamentals',
14 }
15 16 def getRanking(isbn):
17 with uopen('{0}{1}'.format(AMZN, isbn)) as page:
18 return str(REGEX.findall(page.read())[0], 'utf-8')
19:
20: def _main():
21: print('At', ctime(), 'on Amazon...')
22: with ThreadPoolExecutor(3) as executor:
23: for isbn, ranking in zip(
24: ISBNs, executor.map(getRanking, ISBNs)):
25: print('- %r ranked %s' % (ISBNs[isbn], ranking)
26: print('all DONE at:', ctime())
27:
28: if __name__ == '__main__':
29: main()
Построчное объяснение
Строки 1–14
Если не считать новой инструкции import, то вся первая половина этого сцена- рия полностью идентична той, что приведена в файле bookrank3.py, который рас- сматривался выше в главе.
Строки 16–18
В новой функции getRanking() используются инструкция with и функция str.
format(). Аналогичные изменения можно внести в сценарий bookrank.py, посколь- ку оба указанных средства доступны также в версии 2.6 и последующих (а не пред- усмотрены исключительно в версиях 3.x).
Строки 20–26
В предыдущем примере кода использовался метод executor.submit() для фор- мирования заданий. В данном примере предусмотрены некоторые изменения в свя- зи с использованием метода executor.map(), поскольку он позволяет реализовать функции из _showRanking() и полностью исключать их поддержку из нашего кода.
Полученный вывод почти аналогичен тому, который рассматривался ранее:
$ python3 bookrank3CF.py
At Wed Apr 6 00:21:50 2011 on Amazon...
- 'Core Python Programming' ranked 43,992
- 'Python Fundamentals' ranked 1,018,454
- 'Python Web Development with Django' ranked 502,566 all DONE at: Wed Apr 6 00:21:55 2011 06_ch04.indd 231 22.01.2015 22:00:51

Глава 4

Многопоточное программирование
232
Дополнительные сведения об истории создания модуля concurrent.futures можно найти с помощью приведенных ниже ссылок.
http://docs.python.org/dev/py3k/library/concurrent.futures.html http://code.google.com/p/pythonfutures/ http://www.python.org/dev/peps/pep-3148/
Краткое описание этих параметров, а также другая информация, касающаяся мо- дулей и пакетов для многопоточной организации программы, приведена в следую- щем разделе.
1   2   3   4   5   6   7   8

4.10. Связанные модули
В табл. 4.6 перечислены некоторые модули, которые можно использовать при программировании многопоточных приложений.
Таблица 4.6. Стандартные библиотечные модули, связанные с многопоточной поддержкой
Модуль
Описание
thread a
Основной, находящийся на низком уровне модуль поддержки потоков threading
Объекты для многопоточной организации работы и синхронизации высо- кого уровня multiprocessing b
Запуск/использование подпроцессов с помощью интерфейса threading subprocess c
Полный отказ от потоков и выполнение вместо них процессов
Queue
Синхронизированная очередь с последовательной организацией для не- скольких потоков mutex d
Объекты мьютексов concurrent.futures e
Библиотека высокого уровня для асинхронного выполнения
SocketServer
Создание/управление многопоточными серверами TCP или UDP
a
Переименован в _thread в Python 3.0.
b
Новое в версии Python 2,6.
c
Новое в версии Python 2.4.
d
Обозначен как устаревший в Python 2.6 и удален в версии 3.0.
e
Впервые введенный в Python 3.2, но предоставляемый вне стандартной библиотеки для версии 2.6 и последующих версий.
4.11. Упражнения
4.1. Сопоставление процессов с потоками. В чем состоят различия между процес- сами и потоками?
4.2. Потоки Python. Какие типы многопоточных приложений обеспечивают наибольшую производительность в Python, ограничиваемые пропускной способностью ввода-вывода или пропускной способностью процессора?
4.3. Потоки. Какие наиболее заметные отличия будут обнаружены после за- пуска многопоточного приложения в системе не с одним, а с несколькими процессорами? Как, по вашему мнению, будут выполняться несколько по- токов в этих системах?
06_ch04.indd 232 22.01.2015 22:00:52

233 4.11. Упражнения
4.4. Потоки и файлы.
а) Создайте функцию, которая получает байтовое значение и имя файла
(в виде параметров или данных, введенных пользователем) и определяет, сколько раз байтовое значение появляется в файле.
б) Теперь предположим, что входной файл является чрезвычайно большим.
Допускается применение для чтения файла одновременно нескольких функций, поэтому измените полученное ранее решение в целях созда- ния многочисленных потоков, обрабатывающих разные части файла, что- бы каждый поток отвечал лишь за определенную часть файла. Соберите данные, полученные каждым потоком, и рассчитайте суммарное значе- ние. Воспользуйтесь модулем timeit для измерения продолжительности работы обоих решений, однопоточного и многопоточного, и прокоммен- тируйте разницу в производительности, если таковая будет обнаружена.
4.5. Потоки, файлы и регулярные выражения. Для выполнения этого задания тре- буется очень большой файл почтового ящика. Если таковой отсутствует, со- едините все свои сообщения электронной почты в общий текстовый файл.
Задание заключается в следующем. Воспользуйтесь регулярными выраже- ниями, предназначенными для распознавания адресов электронной почты и URL веб-сайтов, которые рассматривались ранее в этой книге, для преоб- разования всех адресов электронной почты и URL из указанного большого файла в актуальные ссылки. Эти ссылки необходимо сохранить в отдельном файле с расширением .html (или .htm), который можно открыть в веб-бра- узере для получения возможности переходить по ним с помощью щелчка мышью. Используйте потоки для проведения процесса преобразования од- новременно по всему большому текстовому файлу, после чего соберите по- лученные результаты в отдельный новый файл .html. Откройте этот файл в веб-браузере, чтобы проверить, действительно ли работают ссылки.
4.6. Потоки и сети. В приложении службы интерактивной переписки, разрабо- танном в качестве упражнения в предыдущей главе, требовалось использо- вание в составе решения так называемых тяжеловесных потоков, или про- цессов. Преобразуйте это решение в многопоточное.
4.7. *Потоки и программирование для веб. Приложение Crawler, которое пред- ставлено в главе 10 “Программирование для веб. CGI и WSGI”, является однопоточным приложением, предназначенным для загрузки веб-страниц.
Его усовершенствованию способствовало бы применение многопоточно- го программирования. Обновите сценарий crawl.py (переименовав его в mtcrawl.py), чтобы для загрузки страниц использовались независимые по- токи. Обязательно воспользуйтесь механизмом блокировки того или иного типа для предотвращения конфликтов доступа к очереди ссылок.
4.8. Пулы потоков. Внесите изменения в код сценария prodcons.py, который рассматривается в примере 4.12, чтобы в нем вместо потока производителя и одного потока потребителя могло применяться любое количество пото- ков потребителя (пул потоков) для обработки или выборки в любой мо- мент времени нескольких элементов из очереди Queue.
4.9. Файлы. Создайте ряд потоков для подсчета количества строк в наборе тек- стовых файлов (который может иметь большой суммарный объем). Может
06_ch04.indd 233 22.01.2015 22:00:52


Глава 4

Многопоточное программирование
234
быть предусмотрена возможность выбирать количество используемых пото- ков. Сравните производительность многопоточной и однопоточной версий этого кода. Совет. Ознакомьтесь с упражнениями в конце главы 9 в книге
Core Python Programming или Core Python Language Fundamentals.
4.10. Параллельная обработка. Возьмите за основу свое решение упражнения 4.9 и примите к выбранной вами произвольной задаче, такой как обработка набора сообщений электронной почты, загрузка веб-страниц, обработка веб-каналов RSS или Atom, усовершенствование обработки сообщений сер- вером интерактивной переписки, поиск решения головоломки и т.д.
4.11. Примитивы синхронизации. Изучите каждый из примитивов синхронизации в модуле threading. Опишите их назначение, укажите возможную область их применения и подготовьте примеры рабочего кода для каждого из них.
Следующий ряд упражнений касается сценария candy.py, который рассматри- вался в примере 4.11.
4.12. Перенос в версию Python 3. Возьмите за основу сценарий candy.py и при- мените к нему инструмент 2to3 для создания версии Python 3 с именем candy3.py.
4.13. Модуль threading. Добавьте к сценарию средства отладки. В частности, для приложений, в которых используются семафоры (с начальным значением, как правило, больше 1), можно предусмотреть точное определение значе- ния счетчика в любое конкретное время. Подготовьте вариант сценария candy.py (который можно назвать candydebug.py) и реализуйте в нем воз- можность отображать значение счетчика. Вам может потребоваться изучить исходный код сценария threading.py, как было указано выше в одном из советов. После внесения этих изменений можно откорректировать вывод сценария, чтобы он выглядел примерно так:
$ python candydebug.py starting at: Mon Apr 4 00:24:28 2011
THE CANDY MACHINE (full with 5 bars)!
Buying candy... inventory: 4
Refilling candy... inventory: 5
Refilling candy... full, skipping
Buying candy... inventory: 4
Buying candy... inventory: 3
Refilling candy... inventory: 4
Buying candy... inventory: 3
Buying candy... inventory: 2
Buying candy... inventory: 1
Buying candy... inventory: 0
Buying candy... empty, skipping all DONE at: Mon Apr 4 00:24:36 2011 06_ch04.indd 234 22.01.2015 22:00:52