Когда вы запускаете отдельную программу, ваша операционная система создает один процесс. Он использует системные ресурсы (центральный процессор, память, место на диске) и структуры данных в ядре операционной системы (файлы и сетевые соединения, статистика использования и т. д.). Процесс изолирован от других процессов — он не может видеть, что делают другие процессы, или мешать им.
Операционная система отслеживает все запущенные процессы, давая каждому из них немного времени, а затем переключаясь на другие, для того чтобы справедливо распределять работу и реагировать на действия пользователя. Вы можете увидеть состояние своих процессов с помощью графического интерфейса вроде Mac’s Activity Monitor (OS X) или Диспетчера задач в Windows.
Вы также можете получать данные о процессах собственных программ. Модуль стандартной библиотеки os помогает получить некоторую системную информацию. Например, следующие функции позволяют получить идентификатор процесса и текущую рабочую папку запущенного интерпретатора Python:
>>> import os
>>> os.getpid()
76051
>>> os.getcwd()
'/Users/williamlubanovic'
А эти — мои идентификаторы пользователя и группы:
>>> os.getuid()
501
>>> os.getgid()
20
Создаем процесс с помощью модуля subprocess
Все программы, с которыми вы сталкивались до этого момента, представляли собой отдельные процессы. Вы можете запускать и останавливать остальные существующие программы с помощью Python, используя модуль subprocess. Если вы хотите просто запустить другую программу в оболочке и получить результат ее работы (стандартный отчет о работе и отчет об ошибках), используйте функцию getoutput(). В этом примере мы получим результат работы программы date системы Unix:
>>> import subprocess
>>> ret = subprocess.getoutput('date')
>>> ret
'Sun Mar 30 22:54:37 CDT 2014'
Вы не получите результат, пока процесс не завершится. Если вам нужно вызвать что-то, что может занять много времени, обратитесь к разделу «Конкуренция» главы 11. Поскольку аргументом функции getoutput() является строка, представляющая собой команду оболочки, вы можете включить аргументы, каналы, перенаправление ввода/вывода и т. д.:
>>> ret = subprocess.getoutput('date — u')
>>> ret
'Mon Mar 31 03:55:01 UTC 2014'
Передача строки-отчета команде wc насчитывает одну строку, шесть «слов» и 29 символов:
>>> ret = subprocess.getoutput('date — u | wc')
>>> ret
'·······1·······6······29'
Метод check_output() принимает список команд и аргументов. По умолчанию он возвращает объект типа bytes, а не строки и не использует оболочку:
>>> ret = subprocess.check_output(['date', '-u'])
>>> ret
b'Mon Mar 31 04:01:50 UTC 2014\n'
Чтобы показать статус выхода другой программы, используйте функцию getstatusoutput(), которая возвращает кортеж, содержащий код статуса и результат работы:
>>> ret = subprocess.getstatusoutput('date')
>>> ret
(0, 'Sat Jan 18 21:36:23 CST 2014')
Если вам нужен не результат работы программы, а только код, используйте функцию call():
>>> ret = subprocess.call('date')
Sat Jan 18 21:33:11 CST 2014
>>> ret
0
(В системах семейства Unix 0 обычно является статусом, сигнализирующим об успехе.)
Эти дата и время были выведены на экран, но не получены нашей программой. Поэтому мы сохраняем код возврата как ret.
Вы можете запускать программы с аргументами двумя способами. Первый заключается в том, чтобы разместить их в одной строке. Для примера возьмем команду date — u, которая выводит на экран дату и время в UTC (о UTC мы поговорим немного позже):
>>> ret = subprocess.call('date — u', shell=True)
Tue Jan 21 04:40:04 UTC 2014
Вам нужно использовать значение shell=True, чтобы распознать команду date — u, разбив ее на отдельные строки и, возможно, расширяя любые символы подстановки вроде * (в нашем примере мы их не использовали).
Во втором варианте мы создаем список аргументов, поэтому нам не нужно вызывать оболочку:
>>> ret = subprocess.call(['date', '-u'])
Tue Jan 21 04:41:59 UTC 2014
Создаем процесс с помощью модуля multiprocessing
Вы можете запустить функцию Python как отдельный процесс или даже несколько независимых процессов с помощью модуля multiprocessing. Рассмотрим короткий пример, который не делает ничего полезного. Сохраните его под именем mp.py, а затем запустите его, введя команду python mp.py:
import multiprocessing
import os
def do_this(what):
····whoami(what)
def whoami(what):
····print("Process %s says: %s" % (os.getpid(), what))
if __name__ == "__main__":
····whoami("I'm the main program")
····for n in range(4):
········p = multiprocessing.Process(target=do_this,
··········args=("I'm function %s" % n,))
········p.start()
Когда я запускаю этот пример, то вижу на экране следующее:
Process 6224 says: I'm the main program
Process 6225 says: I'm function 0
Process 6226 says: I'm function 1
Process 6227 says: I'm function 2
Process 6228 says: I'm function 3
Функция Process() породила новый процесс и запустила в нем функцию do_this(). Поскольку мы делали это в цикле с четырьмя итерациями, мы сгенерировали четыре новых процесса, которые выполнили методы do_this() и завершились.
Модуль multiprocessing имеет множество возможностей. Он предназначен для тех случаев, когда вам нужно разбить некую задачу на несколько процессов, чтобы сэкономить время, например загрузить веб-страницу для получения с нее данных, изменить размер изображений и т. д. Он содержит способы разместить задачи в очередь, позволить процессам общаться и подождать, пока все процессы завершатся. В разделе «Конкуренция» главы 11 содержится более подробная информация.
Убиваем процесс с помощью функции terminate()
Если вы создали один или несколько процессов и по какой-то причине хотите завершить один из них (возможно, он застрял в цикле, или вам стало скучно, или вы хотите побыть жестоким правителем), используйте функцию terminate(). В следующем примере наш процесс должен досчитать до миллиона, засыпая после каждого шага на секунду и выводя раздражающее сообщение. Однако у нашей основной программы заканчивается терпение, и она сбивает его с орбиты:
import multiprocessing
import time
import os
def whoami(name):
····print("I'm %s, in process %s" % (name, os.getpid()))
def loopy(name):
····whoami(name)
····start = 1
····stop = 1000000
····for num in range(start, stop):
········print("\tNumber %s of %s. Honk!" % (num, stop))
········time.sleep(1)
if __name__ == "__main__":
····whoami("main")
····p = multiprocessing.Process(target=loopy, args=("loopy",))
····p.start()
····time.sleep(5)
····p.terminate()
Когда я запускаю эту программу, я вижу следующее:
I'm main, in process 97080
I'm loopy, in process 97081
····Number 1 of 1000000. Honk!
····Number 2 of 1000000. Honk!
····Number 3 of 1000000. Honk!
····Number 4 of 1000000. Honk!
····Number 5 of 1000000. Honk!
Календари и часы