【问题标题】:How to return to the main thread from a callback called on another thread如何从另一个线程上调用的回调返回主线程
【发布时间】:2016-02-22 09:53:42
【问题描述】:

我想将我的代码从后台回调执行到主线程。由于某些原因,我没有找到执行该操作的简单示例。

我发现了队列和池的例子,这显然不是我想要的。我只想加入主线程以执行我的功能。

下面是一些简单的代码:

def my_callback:
    # this callback is called from a third party and I don't have control on it. 
    # But because of this, the function must_be_executed_on_main_thread is
    # executed on the background.
    must_be_executed_on_main_thread()

def must_be_executed_on_main_thread:
    # must be executed on the main thread.

** 编辑 **

这是我的问题: 主文件:

if __main__ == '__main__'
    sio = socketio.Server()
    app = Flask(__name__)
    app = socketio.Middleware(sio, app)

    # now the thread is blocking into the server pool
    eventlet.wsgi.server(eventlet.listen('', 7000)), app)

从另一个文件中,我有一个处理套接字事件的装饰器:

@sio.on('test')
def test(sid, data):
    print threading.currentThread()
    sio.emit('event triggered')

这就是问题所在:我有一些由硬件按钮触发的事件,这些事件触发了调用 sio“测试”事件的回调。但是由于事件没有触发到同一个线程中,这给我带来了一些麻烦:

# async call !
def event_cb(num):
    sio.emit('test')

GPIO.add_event_detect(btNumber, GPIO.RISING, event_cb)

测试方法装饰器被调用: 但是当发射不在主线程上时,它会停止工作。

只要从 主线程 完成套接字调用,就可以了。 但是当一个调用在 DummyThread 上完成时,这不再起作用了。

我在使用 socket.io 实现的客户端设备上进行测试。只要在主线程上完成“发射”,它就可以工作,但是当我在另一个线程上执行时(比如触发回调的按钮,它就会停止工作)

这就是我喜欢表演的原因

【问题讨论】:

  • “停止工作”并不是对问题的明确描述。如果您不希望wsgi.server 阻塞主线程,请执行eventlet.spawn(eventlet.wsgi.server, listen(...), app)。不要忘记定期调用eventlet.sleep(0) 以确保 Eventlet 主循环有机会处理网络事件。另外,你可以试试monkey_patch(threading=True),这样就会有一个操作系统线程。还要注意button bounce(谷歌搜索)。使用按钮的正确方法是 Linux gpio-keys 或通过采样手动去抖动。
  • 根据性能要求自己执行循环(而不是某些库在 C 中)可能可行,也可能不可行。不过,它肯定会起作用。 :)

标签: python multithreading eventlet


【解决方案1】:

您可以使用Queue 实例轻松地在线程之间传递值。

这个简单的演示脚本解释了这个概念。您可以使用 Ctrl-C 中止脚本。

#!/usr/bin/env python
import Queue
import random
import threading
import time

def main_thread():
    """Our main loop in the main thread.

    It receives the values via a Queue instance which it passes on to the
    other threads on thread start-up.
    """
    queue = Queue.Queue()

    thread = threading.Thread(target=run_in_other_thread,
                              args=(queue,))
    thread.daemon = True  # so you can quit the demo program easily :)
    thread.start()

    while True:
         val = queue.get()
         print "from main-thread", val

def run_in_other_thread(queue):
    """Our worker thread.

    It passes it's generated values on the the main-thread by just
    putting them into the `Queue` instance it got on start-up.
    """
    while True:
         queue.put(random.random())
         time.sleep(1)

if __name__ == '__main__':
    main_thread()

【讨论】:

  • 感谢您的回复。不幸的是,我不能这样设计我的代码:我必须在主线程上运行一个 eventlet.wsgi.server()。这个运行一个阻塞主线程的服务器。而且因为没有事件循环,我有点卡住了。
  • @MrBonjour 好的。请修改您的问题以包含所有相关信息。
  • 是的,我其实有点迷茫。我正在尝试实现您示例的派生。这很接近,但由于 eventlet,我试图从同一个线程调用我的套接字对象。我将修改我的问题,因为我认为您可以通过他的事件循环通过线程重新插入数据。但在这种情况下不存在。
  • Eventlet中有事件循环。当任何 greenthread 将要休眠或等待网络 IO 或另一个事件时,它就会运行。队列是同步操作系统线程的最正确方法,应该适合您。有问题的代码示例没有提到 Queue 是如何工作的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-20
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 1970-01-01
相关资源
最近更新 更多