Q. Завершить процесс, вызванный в функции из другой функции в Python - PullRequest
0 голосов
/ 20 февраля 2019

Я написал функцию fun0, которая вызывает:

  1. подпроцесс p1,
  2. функция fun1,
  3. и функция fun2, который вызывает другой процесс p2.

Два процесса p1 и p2 являются внешними файлами.Код функции fun0:

def fun0():

    # call the 1. process        
    p1 = subprocess.Popen(["python", "script1.py"])
    try:
        p1.wait()
    except KeyboardInterrupt:
        try:
            p1.terminate()
        except OSError:
            pass
        p1.wait()

    # call the 1. function
    fun1()

    # loop 3 times
    for i in range(1, 3, 1):   

        # call the 2. function
        fun2()

def fun2():

    # call 2. process
    p2 = subprocess.Popen(["python", "script2.py"])
    try:
        p2.wait()
    except KeyboardInterrupt:
        try:
            p2.terminate()
        except OSError:
            pass
        p2.wait()

. Script_2.py использует многопоточность для запуска двух функций одновременно.Код выглядит следующим образом:

import threading

def Ref():
    read ref. value
    return ref. value

def Read():
    while read_interval <= some_time:
        read value
        yield value

def Add():
    while delta > error:
        while delta > limit :
            while True:
                value = next(Read())
                delta = change delta
                check conditions
        while True:
            value = next(Read())
            delta = change delta
            check conditions
    return result

if __name__ == '__main__':

    t0 = threading.Thread(target = Ref)
    t0.start()
    t0.join()

    readTime = datetime.now()

    t1 = threading.Thread(target = Read)
    t2 = threading.Thread(target = Add)

    t1.start()
    t2.start()

    t1.join()
    t2.join()

Я хотел бы остановить выполнение функции fun0() извне, т.е. из другой функции.Когда происходит остановка, я также хотел бы, чтобы функции fun1, fun2 и процессы p1, p2 останавливали и, возможно, извлекали из них данные.Интересно, какой будет элегантный, чистый и питонский способ сделать это?Я рассматриваю:

  1. многопоточность,
  2. многопроцессорность,
  3. с использованием другой функции,
  4. с использованием сигналов?

Я прочитал в этом посте 28906558 о том, как остановить функцию с помощью multiprocessing, это должно быть способом сделать это, но я хотел бы услышать больше мнений, спасибо.

Ответы [ 4 ]

0 голосов
/ 14 марта 2019

После некоторых проб и ошибок я получил работающий код.Я чувствую, что есть много места для улучшения.Код основного скрипта:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-

"""
This is an example of a simple terminaton of a subprocess with a ctrl+c. 
"""

import os
import time
import signal
import subprocess
import multiprocessing
from datetime import datetime


p1_pid = 0
proc_pid = 0


def signal_handler(signal, frame):
    print " main signal handler "
    # write to log
    with open('.../Python2.7/log.txt', 'a') as log:
            log.write(str(datetime.now()) + ' exiting: main function ' + "\n")
    exit(2)


def f1():
    for i in range(5,0,-1):
        print "f1 > main function", i
        # write to log
        with open('/home/parovelb/Desktop/Python2.7/log.txt', 'a') as log:
            log.write(str(datetime.now()) + ' main function ' + str(i) + "\n")
        time.sleep(1)


def execute():
    # call the function second
    f1()

    # call the process first
    global p1, p1_pid
    p1 = subprocess.Popen(["python", "eg_test.py"], shell=False)
    p1_pid = p1.pid
    print "p1_pid", p1_pid
    try:
        p1.wait()
    except KeyboardInterrupt:
        try:
            p1.terminate()
        except OSError:
           pass


def kill_them_all():
    time.sleep(10)
    print "p1_pid", p1_pid
    os.kill(p1_pid,signal.SIGINT)
    os.kill(proc_pid,signal.SIGINT)


def main():
    # define signal handler
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    global proc, proc_pid    

    proc = multiprocessing.Process(target=execute)
    proc_pid = proc.pid
    print "proc_pid", proc_pid
    proc_end = multiprocessing.Process(target=kill_them_all)

    proc.start()
    proc_end.start()

    proc.join()
    proc_end.join()


main()

Код внешнего скрипта:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-


import time
import signal
from datetime import datetime


def signal_handler(signal, frame):
    print " external signal handler "
    with open('.../Python2.7/log.txt', 'a') as log:
            log.write(str(datetime.now()) + ' exiting: external function ' + "\n")
    exit(2)


def main():
    #define the signal handler
    signal.signal(signal.SIGINT, signal_handler)

    # simple for loop countdown
    for i in range(20,0,-1):
        print "eg_test.py > external file > main function", i
        with open('.../Python2.7/log.txt', 'a') as log:
            log.write(str(datetime.now()) + ' external function ' + str(i) + "\n")
        time.sleep(1)


main()
0 голосов
/ 01 марта 2019

Я изменил код основной программы, используя pMain.daemon = True перед pMain.start(), как предлагалось в этой теме , но процесс p1 все еще выполняется в фоновом режиме даже после выхода.

import os
import time
import signal
import subprocess
from subprocess import Popen, PIPE
import multiprocessing
from datetime import datetime

def fun0():

    p1 = subprocess.Popen(["python", "eg_script1_countdown1.py"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
    #p1 = subprocess.Popen(["python", "eg_script1_countdown1.py"])
    global p1_pid
    p1_pid = p1.pid
    print "p1_pid:", p1_pid

    try:
        p1.wait()
    except KeyboardInterrupt:
       try:
            p1.terminate()
       except OSError:
            pass
       p1.wait()

    # call fun1
    fun1()

    # loop 3 times
    for i in range(3):
        # call fun2
        print "call fun2, loop n.", i
        with open('/home/parovelb/Desktop/Python2.7/log.txt', 'a') as log:
                log.write(str(datetime.now()) + '  for loop n. ' + str(i) + "\n")
        fun2()

def fun1():
    for i in range(5,0,-1):
        print "fun1 > take five in", i
        with open('/home/parovelb/Desktop/Python2.7/log.txt', 'a') as log:
                log.write(str(datetime.now()) + '  fun1 ' + str(i) + "\n")
        time.sleep(1)

def fun2():

    # start process2
    p2 = subprocess.Popen(["python", "eg_script2_countdown2.py"], stdin=PIPE, stdout=PIPE, stderr=PIPE)
    #p2 = subprocess.Popen(["python", "eg_script2_countdown2.py"])
    global p2_pid
    p2_pid = p2.pid

    try:
        p2.wait()
    except KeyboardInterrupt:
        try:
            p2.terminate()
        except OSError:
            pass
        p2.wait()


if __name__ == '__main__':

    # original code
    pMain = multiprocessing.Process(target=fun0)
    pMain.daemon = True
    pMain.start()
    time.sleep(10)    
    pMain.terminate()
    exit()
0 голосов
/ 07 марта 2019

Следуя предложенным решениям в сообщениях Delegate-Sigint-Signal ... и делегат-обработки сигналов ... Я изменил код моей основной программы:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-

"""
This is an example of a simple terminaton of a subprocess with a ctrl+c. 
"""

import time
import signal
import subprocess


def signal_handler(signal, frame):
   print "outer signal handler"
   exit(2)


def fun1():
   for i in range(5,0,-1):
        print "fun1 > take five in", i
        time.sleep(1)


def execute():

    # call the process first
    proc = subprocess.Popen("python eg_test.py",shell=True)
    try:
        proc.wait()
    except KeyboardInterrupt:
        try:
            proc.terminate()
        except OSError:
            pass

    # call the function second
    fun1()


def main():

    signal.signal(signal.SIGINT, signal_handler)

    execute()
    time.sleep(5)
    proc.send_signal(signal.SIGINT)


main()

Я также изменил только один из внешних сценариев только для тестового запуска:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-

"""
This is an example of a simple for loop countdown run a subprocess and
terminated with ctrl+c. 
"""

import time
import signal
from datetime import datetime


def signal_handler(signal, frame):
    print "exiting: inner function"
    exit(2)



def main():
    #define the signal handler
    signal.signal(signal.SIGINT, signal_handler)

    # simple for loop countdown
    for i in range(20,0,-1):
        print "test.py > countdown", i
        time.sleep(1)


main()

При нажатии ctrl + c в середине процесса (внешний сценарий выполняется) он завершаетсязатем он продолжает выполнение fun1().Остается вопрос: как завершить функцию execute() из другой функции?

0 голосов
/ 25 февраля 2019

Для этой задачи я использовал простые обратные отсчеты для функции fun1 и для подпроцессов p1 и p2.Затем я начал экспериментировать с функцией как технологическим решением.Код основной программы:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-

#An example of how to terminate the execution of a function
#which calls a process using an external trigger.

import time
import subprocess
from multiprocessing import Process, Queue

def fun0():

    # start process1        
    p1 = subprocess.Popen(["python", "eg_script1_countdown1.py"])
    p1_PID = p1.pid
    print "p1_PID:", p1_PID
    try:
        p1.wait()
    except KeyboardInterrupt:
        try:
            p1.terminate()
        except OSError:
            pass
        p1.wait()

    # call function1
    fun1()

    # loop 3 times
    for i in range(1, 3, 1):
        # call fun2
        print "call function 2, loop n.", i
        fun2()

def fun1():
    for i in range(5,0,-1):
        print "fun1 > take five in", i
        time.sleep(1)

def fun2():
    # start process2
    p2 = subprocess.Popen(["python", "eg_script2_countdown2.py"])
    p2_PID = p2.pid
    print "p2_PID:", p2_PID
    try:
        p2.wait()
    except KeyboardInterrupt:
        try:
            p2.terminate()
        except OSError:
            pass
        p2.wait()

if __name__ == '__main__':
    pMain = Process(target=fun0)
    pMain_PID = pMain.pid
    print "pMain_PID:", pMain_PID
    pMain.start()
    time.sleep(20)
    pMain.terminate()

Код первого вызываемого файла:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-
#eg_script1_countdown.py

import time

for i in range(10,0,-1):
    print "script1.py > have a beer in", i
    time.sleep(1)

и второго файла:

#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-
#eg_script2_countdown.py

import time

for i in range(10,0,-1):
    print "script2.py > give love in", i
    time.sleep(1)

Я изменяю строку time.sleep(20) в __name__ == '__main__', чтобы увидеть, как внутреннее завершение pMain.terminate() влияет на результат.Я обнаружил, что:

  1. при запуске во время работы подпроцесса p1, он не завершает его,
  2. при запуске во время работы fun1(), онзавершить функцию,
  3. при запуске во время работы подпроцесса p2, он не завершает процесс, а завершает fun2() в следующем цикле.

Как завершитьподпроцессы p1 и p2 во время работы?

...