根据需要从jquery事件中选择性关闭各个子python进程

时间:2019-03-01 16:57:03

标签: javascript python jquery flask multiprocessing

我从未在this question中收到答案,所以我继续着通过其中描述的数据库在python进程之间共享数据的方法。现在,我的问题是从客户端Web浏览器jquery事件中有选择地,干净地(或根本没有)停止子进程而不终止主父进程。我的示例代码如下。 Flask通过按钮提供了一个简单的网页。当按下按钮时,将创建一个新的python进程,并获取数据。但是,再次按下按钮(停止数据获取)时,抛出了UnboundLocalError: local variable 'acquire_process' referenced before assignment异常。此外,在另一个终端上运行$ ps -ef | grep python命令时,主进程和创建的子进程都仍在运行,直到我抛出KeyboardInterrupt为止。但是,这也杀死了主父进程,在我的实际项目中,该主进程需要继续向客户端提供其他内容。

我的问题是:按需从客户端jquery事件中有选择地启动和停止单个子python进程的最佳方法是什么?

#!/usr/bin/env python
#  -*- coding: utf-8 -*-

import time, os, random
import socket as sock
import psycopg2 as sql
import multiprocessing as mp
from flask_socketio import SocketIO, emit
from flask import Flask, render_template, url_for

from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT

app = Flask(__name__)
socket = SocketIO(app)

#Get the server's IP on which to serve app, client can navigate to IP
def get_ip_address():
    """ Utility function to get the IP address of the device. """
    ip_address = '127.0.0.1'  # Default to localhost
    s = sock.socket(sock.AF_INET, sock.SOCK_DGRAM)
    try:
        s.connect(('1.1.1.1', 1))  # Does not have to be reachable
        ip_address = s.getsockname()[0]
    finally:
        s.close()
    return ip_address

#create a table to store acquired data, erasing old data
def build_table():
    conn_main = sql.connect('dbname=mp_example user=pi')
    conn_main.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
    cm = conn_main.cursor()
    cm.execute('DROP TABLE IF EXISTS DAQ_example;')
    cm.execute('CREATE TABLE DAQ_example \
        (id SERIAL PRIMARY KEY,\
        ch0 VARCHAR,\
        ch1 VARCHAR,\
        ch2 VARCHAR,\
        ch3 VARCHAR);')
    cm.close()
    conn_main.close()
    print('table built')

#function run in the process for acquiring data
def acquire_data():
    print('acquire_data function called')
    conn_fill = sql.connect('dbname=mp_example user=pi')
    conn_fill.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
    cf = conn_fill.cursor()
    while True:
        ch0 = random.random()
        ch1 = random.random()
        ch2 = random.random()
        ch3 = random.random()
        cf.execute('INSERT INTO DAQ_example\
            (ch0, ch1, ch2, ch3)\
            VALUES (%s, %s, %s, %s);', (ch0, ch1, ch2, ch3))
        time.sleep(0.001)

#When client requests homepage
@app.route('/')
def render_index():
    #serve the html
    return render_template('index.html')

#Listen for client to start measurement    
@socket.on('control_measurement_process')
def start_process(state):
    #start a process to acquire data
    if state == True:
        build_table()
        acquire_process = mp.Process(target=acquire_data)
        acquire_process.start()
        print('acquire_process')
        print(os.system("ps -ef | grep python"))
    #attempt to clean up acquire_process
    elif state == False:
        print('terminate bool called')  
        acquire_process.terminate()
        print('acquire_process.terminate()')
        print(os.system("ps -ef | grep python"))

if __name__ == '__main__':
    print('main process')
    print(os.system("ps -ef | grep python"))
    #mp.set_start_method('spawn')
    socket.run(app, host=get_ip_address(), port=8080)

“ index.html”如下:

<!DOCTYPE html>
<html>
<head>
    <link
        rel="stylesheet"
        href="{{ url_for('static', filename='css/style.css') }}"
        >
    <link
        rel="stylesheet"
        href="{{ url_for('static', filename='bs/bootstrap-grid.css') }}"
        >
    <script
        src="{{ url_for('static', filename='js/jquery-3.3.1.js') }}"
        ></script>
    <script
        src="{{ url_for('static', filename='js/socket.io.js') }}"
        ></script>
    <script
        src="{{ url_for('static', filename='js/bokeh-0.12.15.min.js') }}"
        ></script>
</head>
<body>
    <div id="controls_title">
        Controls:
    </div>
    <div id="controls" class="row">
        <button id="measure" class="col-3">start<br>measurement</button>
    </div>

    <script>
    <!-- jQuery here -->

        var socket = 
            io.connect(location.origin, {transport: ['websocket']});
        var allow_measurement = true;

        //listens for client to start measurement acquisition
        $("#measure").on("click", function() {
            if ($("#measure").text() == "startmeasurement") {
                $("#measure").html("stop<br>measurement");
                allow_measurement = 1;
                socket.emit("control_measurement_process",
                            state=allow_measurement);
            }
            else {
                $("#measure").html("start<br>measurement");
                allow_measurement = 0;
                socket.emit("control_measurement_process",
                            state=allow_measurement);
            }
        });

    </script>
</body>
</html>

1 个答案:

答案 0 :(得分:0)

我终于意识到,每次按下jquery按钮时,start_process函数都会被调用干净,这就是为什么尚未分配局部变量的原因。我还找到了一种通过将start_process函数更改为以下内容来至少“终止”该过程的方法:

#Listen for client to start or stop the measurement    
@socket.on('control_measurement_process')
def start_process(state):
#start a process to acquire data
if state == True:
    build_table()
    acquire_process = mp.Process(target=acquire_data,
        name='acquire_process')
    acquire_process.start()
    print('acquire_process')
    print(os.system("ps -ef | grep python"))
#attempt to clean up acquire_process
elif state == False:
    print('terminate bool called') 
    #get all active children 
    children = mp.active_children()
    for p in children:
        #select only the process I want to stop
        if p.name = 'acquire_process':
            #at least defunct it
            p.terminate()
    print('acquire_process.terminate()')
    print(os.system("ps -ef | grep python"))