Простой Python — страница 37 из 66

Когда вы запускаете отдельную программу, ваша операционная система создает один процесс. Он использует системные ресурсы (центральный процессор, память, место на диске) и структуры данных в ядре операционной системы (файлы и сетевые соединения, статистика использования и т. д.). Процесс изолирован от других процессов — он не может видеть, что делают другие процессы, или мешать им.

Операционная система отслеживает все запущенные процессы, давая каждому из них немного времени, а затем переключаясь на другие, для того чтобы справедливо распределять работу и реагировать на действия пользователя. Вы можете увидеть состояние своих процессов с помощью графического интерфейса вроде 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!

Календари и часы