Наткнулся тут недавно на декоратор run_async. Идея мне понравилась и я решил реализовать также декоратор run_subprocess. Пользоваться им исключительно просто: достаточно написать перед объявлением функции @run_subprocess и функция будет запускаться в отдельном процессе.
Например:
if __name__ == '__main__':
@run_subprocess
def print_somedata():
from time import sleep
print 'starting print_somedata'
sleep(2)
print 'print_somedata: 2 sec passed'
sleep(2)
print 'print_somedata: 2+2 sec passed'
sleep(2)
print 'finished print_somedata'
def main():
print_somedata()
print 'back in main'
print_somedata()
print 'back in main'
print_somedata()
print 'back in main'
main()
Вывод:
c:\Projects\Py>decorator.py
back in main
back in main
back in main
c:\Projects\Py>starting print_somedata
starting print_somedata
starting print_somedata
print_somedata: 2 sec passed
print_somedata: 2 sec passed
print_somedata: 2 sec passed
print_somedata: 2+2 sec passed
print_somedata: 2+2 sec passed
print_somedata: 2+2 sec passed
finished print_somedata
finished print_somedata
finished print_somedata
Исходник декоратора:
def run_subprocess(func):
from subprocess import Popen,PIPE
from functools import wraps
import inspect
import sys
fname = inspect.getfile(func)+'.'+func.__name__+'.py'
lines, startline = inspect.getsourcelines(func)
nn = lines[0].find('@')
@wraps(func)
def async_func( *args, **kwargs):
proc = Popen([fname,], executable=sys.executable, stdin=PIPE)
fd = proc.stdin
for line in lines[1:]:
fd.write(line[nn:])
fd.write(func.__name__+'(')
for i in args:
fd.write(repr(i)+', ')
for a,b in kwargs.iteritems():
fd.write(a+'='+repr(b)+', ')
fd.write(')')
return proc
return async_func
Работает оно так: при старте модуля в декораторе сохраняется исходник декорируемой функции. Это можно сделать благодаря модулю inspect. Далее при старте функции запускается интерпретатор питона, на stdin которого подается исходник функции и параметры запуска. Декоратор возвращает экземпляр класса Popen, который можно сохранить например с целью подождать завершения процессов.
Решение не особо хорошее, но рабочее. Может ктонибудь предложит более элегантный подход?
Здравствуйте, Сергей, Вы писали:
С>В линуксе/бсд — просто fork(). С>Никаких плясок с сохранением исходника функции.
Так ведь fork создает копию процесса, но не позволяет вызвать конкретную функцию, и чтобы это сделать надо писать обработчик командной строки. У декоратора в этом плане огромный плюс — 1 строчка кода и все дела. Замечу еще, что этот декоратор позволяет также передавать аргументы функции. Чтобы это повторить с помощью форка понадобится написать примерно столько же кода, сколько в этом декораторе. Поэтому мне так и понравился этот подход.
Re[3]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Critical Error, Вы писали:
CE>Так ведь fork создает копию процесса, но не позволяет вызвать конкретную функцию, и чтобы это сделать надо писать обработчик командной строки. У декоратора в этом плане огромный плюс — 1 строчка кода и все дела. Замечу еще, что этот декоратор позволяет также передавать аргументы функции. Чтобы это повторить с помощью форка понадобится написать примерно столько же кода, сколько в этом декораторе. Поэтому мне так и понравился этот подход.
Я имею ввиду — для реализации твоего декоратора можно использовать fork(). Вместо Popen() со скармливанием исходника можно сделать fork() и вызов декорируемой функции в потомке.
Re[4]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Сергей, Вы писали:
С>Я имею ввиду — для реализации твоего декоратора можно использовать fork(). Вместо Popen() со скармливанием исходника можно сделать fork() и вызов декорируемой функции в потомке.
То есть вот так будет работать?
if __name__ == '__main__':
def print_somedata():
from time import sleep
print 'starting print_somedata'
sleep(2)
print 'print_somedata: 2 sec passed'
def main():
os.fork() # после этого уже имеем 2 корректно работающих процесса
print_somedata()
print 'back in main'
main()
Re[5]: [Python] Декораторы run_async и run_subprocess.
Re[2]: [Python] Декораторы run_async и run_subprocess.Ошибаетесь полноценный fork работает не так. Дочерний процесс запускаеться как раз сразу после исполнения fork. Поясню:
pid = fork()
if(pid == 0)
{
//вот здесь уже child рабоает
}
else
{
//а здесь все ещё paretn
};
Posted via RSDN NNTP Server 2.1 beta
Re[6]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Сергей, Вы писали:
С>Здравствуйте, Critical Error, Вы писали:
CE>>То есть вот так будет работать?[...]
С>Ну там import os надо добавить, а так — работать будет.
Да, штука хорошая, жаль не мультиплатформенная. Или можно чтото подобное на винде сделать?
Кстати еще вот такой вопрос: как я понимаю форк делает клон процесса с расшаренными дискрипторами и памятью. Как в таком случае будет работать питон? Его объекты будут склонированы 1 к 1? Что если программа которая делает форк является GUI приложением? Появится ли клон окна? В общем хорошо ли это — клонировать все объекты питона в дочернем процессе?
В общем как мне кажется моя версия декоратора имеет право на жизнь и на линуксе тоже, так как позволяет создать "чистый" процесс без лишних объектов, который выполнял бы конкретную порученную ему задачу.
Re[4]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, meandr, Вы писали:
M>Ошибаетесь полноценный fork работает не так. Дочерний процесс запускаеться как раз сразу после исполнения fork. Поясню:
M>
M>pid = fork()
M>if(pid == 0)
M>{
M> //вот здесь уже child рабоает
M>}
M>else
M>{
M> //а здесь все ещё paretn
M>};
M>
Ага, ну в принципе можно сделать декоратор и на основе форка, жалко у меня нет линукса и протестить невозможно.
Re[7]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Critical Error, Вы писали:
CE>Да, штука хорошая, жаль не мультиплатформенная. Или можно чтото подобное на винде сделать?
В cygwin'е что-то там такое есть, как оно работает и насколько хорошо — мне неизвестно.
CE>Кстати еще вот такой вопрос: как я понимаю форк делает клон процесса с расшаренными дискрипторами и памятью. Как в таком случае будет работать питон? Его объекты будут склонированы 1 к 1?
Не совсем склонированы; насколько я понимаю — у процессов будут общие страницы памяти, которые будут копироваться при необходимости.
По поведению полученных процессов — примерно то же самое, как будто была скопирована вся память.
Я в тонкостях досконально не разбирался, могу и ошибаться.
CE>Что если программа которая делает форк является GUI приложением? Появится ли клон окна?
Вряд ли, скорее всего будут странные спецэффекты при одновременной работе с одним подключением к X-серверу.
CE>В общем хорошо ли это — клонировать все объекты питона в дочернем процессе?
Это не хорошо и не плохо, как обычно всё зависит от целей применения.
Например, в случае декоратора, реализованного fork'ом — функция будет иметь доступ к глобальным объектам. В твоей реализации — нет. Для чего-то хорошо одно, для чего-то другое.
CE>В общем как мне кажется моя версия декоратора имеет право на жизнь и на линуксе тоже, так как позволяет создать "чистый" процесс без лишних объектов, который выполнял бы конкретную порученную ему задачу.
Popen() все равно в итоге вызовет fork() для запуска нового процесса (на линуксе).
Твоя версия несомненно имеет плюс в виде портируемости.
Re[8]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Сергей, Вы писали:
С>Это не хорошо и не плохо, как обычно всё зависит от целей применения. С>Например, в случае декоратора, реализованного fork'ом — функция будет иметь доступ к глобальным объектам. В твоей реализации — нет. Для чего-то хорошо одно, для чего-то другое.
Это да... недостаток иногда. В общем вот код декоратора на основе форка. К сожалению затестить не могу так как использую винду.
def run_fork(func):
from functools import wraps
from os import fork
@wraps(func)
def async_func(*args, **kwargs):
pid = fork()
if pid == 0:
func(*args, **kwargs)
else:
return pid
return async_func
Re: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, Critical Error, Вы писали:
CE>Работает оно так: при старте модуля в декораторе сохраняется исходник декорируемой функции. Это можно сделать благодаря модулю inspect. Далее при старте функции запускается интерпретатор питона, на stdin которого подается исходник функции и параметры запуска. Декоратор возвращает экземпляр класса Popen, который можно сохранить например с целью подождать завершения процессов.
CE>Решение не особо хорошее, но рабочее. Может ктонибудь предложит более элегантный подход?
На модуль multiprocessing (из поставки 2.6) не смотрели?
The God is real, unless declared integer.
Re[2]: [Python] Декораторы run_async и run_subprocess.
Здравствуйте, netch80, Вы писали:
N>На модуль multiprocessing (из поставки 2.6) не смотрели?
Да, посмотрел. Очень мощный модуль. Как я понял для старта он использует os.fork на линуксе и subprocess.Popen на винде. Отличие от моего декоратора заключается в том, что multiprocessing передает интерпретатору весь модуль вместо исходника одной конкретной функции. Непонятно лишь как они смогли заставить интепретатор выполнять нужную функцию.