【问题标题】:How can I run bottle concurrently with an infinite loop?如何在无限循环中同时运行瓶子?
【发布时间】:2017-06-09 20:40:50
【问题描述】:

我正在尝试将 bottle.py 作为脚本内的进程运行,但我很沮丧,因为它没有按预期工作。 这是我的脚本的假设/缩短表示。我尝试添加尽可能多的 cmets。

magball.py

import serial
from bottle import route, run, template, request
import multiprocessing
import logging
import datetime


#Here are the most important variables which will be used in the entire script
global lastcommand #The last command received from bottle web interface
global status #What is the current status

lastcommand = 'Go Home' #Options: Start Drawing, Go Home, Next Drawing
status = 'home' #Options: home, homing (means going home), drawing

def log_this(level, msg)
    #a special log function based on logging library for debugging
    pass # <- details are not important, it is working fine

now = datetime.datetime.now()
timeString = now.strftime("%Y-%m-%d %H:%M")
info = {'title' : 'MAGBALL', 'time': timeString, 'lastcommand': lastcommand, 'status': status} #These are passed to main.tpl
@route('/', method='GET')
@route('/', method='POST')
def index(): #<- This works fine, passing the info to the template file (main.tpl) in views directory
    global lastcommand #<- Isn't this referring to the global lastcommand declared at the beginning?
    if request.method == 'POST':
        lastcommand = request.forms.get("submit") #<- This works fine and lastcommand changes when the button on the web page is clicked    
        log_this('info', (lastcommand + ' clicked on web interface')) #<- I can see in the logs which button is clicked, so, this should mean 
                                                                      #that lastcommand (supposedly the global one) was changed successfully

    return template('main.tpl', info)

p_bottle = multiprocessing.Process(target=run, kwargs=dict(host='0.0.0.0', port=80, debug='True')) #<- I start bottle as a process because I think I have to
p_bottle.daemon = True

def draw_pattern() #<- This function can easily run for hours which is fine/normal for the purpose
                   #Unless lastcommand is not equal to 'Start Drawing' this should run forever
    global status
    global lastcommand
    status = 'drawing' #<- I think this should also refer to the global lastcommand declared at the beginning
    #Opens a random file from a specific directory, 
    for filename in shuffled_directory:
        #reads the file line by line and feeds it to the serial port
        .
        .
        if lastcommand != 'Start Drawing': #<- This never works! Although I see in the logs that lastcommand was changed by index() function
            log_this('warning', 'Last command changed during line feeding.')
            break

def go_home()
    global status
    status = 'homing' #<- I think this should also refer to the global lastcommand declared at the beginning
    #Send some commands to serial port and after that change the value of status
    .
    .
    status = 'home'

while true: #This is the infinite loop that needs to run forever
    global status #Don't these variables also refer to the global variables declared at the beginning? 
    global lastcommand
    p_bottle.start() #The bottle.py web interface runs successfully
    #The following while, if etc never runs. I think this "while true:" statement never gets notified
    #that the values of lastcommand and/or status has changed
    while lastcommand == 'Start Drawing':   
        if status == 'home':
            draw_pattern()
        if status == 'homing':
            time.sleep(5)
        if status == 'drawing':
            pass
    while lastcommand == 'Next Drawing':
        if status == 'home':
            draw_pattern()
        if status == 'homing':
            time.sleep(5)
        if status == 'drawing':
            go_home()
            draw_pattern()
    while lastcommand == 'Go Home':
        if status == 'home':
            pass
        if status == 'homing':
            pass
        if status == 'drawing':
            go_home()

main.tpl

<!DOCTYPE html>
   <head>
      <title>{{ title }}</title>
   </head>
   <body>
      <h1>Magball Web Interface</h1>
      <h2>The date and time on the server is: {{ time }}</h2>
    <h2>The last command is: {{ lastcommand }}</h2>
    <h2>Status: {{ status }}</h2>
    <form name='magballaction' method='POST'>
    <input type="submit" name="submit" value="Start Drawing">
    <input type="submit" name="submit" value="Go Home">
    <input type="submit" name="submit" value="Next Drawing">
    </form>
   </body>
</html>

接下来会发生什么:
瓶子进程运行良好(网页可以访问),当我点击网页上的按钮时,我在日志中看到 lastcommand 的值被网页界面更改了。

但是,最后的无限循环 (while true:) 永远不会收到有关 lastcommand 的值已更改的通知。例如,如果 status = home 和 lastcommand 从 Web 界面更改为“开始绘图”,它不会执行draw_pattern()。它什么都不做。

我尝试将所有内容放在 def index() 函数中 return template('main.tpl', info) 行之前,但随后代码运行,但 Web 界面等待代码完成才能启动。由于draw_pattern() 持续数小时,return template('main.tpl', info) 行实际上没有运行并且网页无法访问。

我也尝试将def index() 作为正常功能运行,但是当我这样做时,唯一运行的是网络接口 (def index())。

那么,我该怎么做才能通知 while 循环 lastcommand 和/或 status 的值发生了变化?

【问题讨论】:

  • 顺便说一句,我是 Python 的新手,所以我可能在这里做一些愚蠢的事情。请原谅我的无知。
  • 如果我在这里遗漏了一些东西,我很抱歉,但是您不需要进程间通信来通知一个进程关于另一个进程的状态吗?
  • SRC 感谢您的回答。你可能是对的。我只是尝试通过使用全局变量来实现进程间通信,但失败了,这就是我的问题和这篇文章。
  • 两个进程之间的全局变量不共享。因此,这不起作用也就不足为奇了。我有点相信多处理在这里对您没有帮助。一旦子进程完成,您就可以在父进程中收集进程的输出。作为一个服务器进程,孩子永远不会完成,因此你不会从它那里得到任何值。在这种情况下您可以使用多线程(您可能会共享变量状态,因此管理竞争条件更加棘手)或者您可以使用类似Tornado
  • 以上是恕我直言。这可能有点错误。这就是为什么我没有写一个真正的答案。您可以查看此SO thread 以查看类似的讨论。 this 是多处理的不错介绍。

标签: python multiprocessing bottle


【解决方案1】:

对于任何有兴趣的人,我已经使用线程而不是多处理解决了这个问题。 所以不要使用这个:

p_bottle = multiprocessing.Process(target=run, kwargs=dict(host='0.0.0.0', port=80, debug='True')) 
p_bottle.daemon = True

我用过:

p_bottle = threading.Thread(target=run, kwargs=dict(host='0.0.0.0', port=80, debug='True'))
p_bottle.daemon = True

现在线程中的函数可以成功更改全局变量,该变量会立即被无限 while true 循环拾取。 谢谢大家的建议。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-05
    • 1970-01-01
    • 2021-10-05
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    相关资源
    最近更新 更多