Python 来自多个方法的调用阻塞方法

Python 来自多个方法的调用阻塞方法,python,python-3.x,asynchronous,async-await,python-asyncio,Python,Python 3.x,Asynchronous,Async Await,Python Asyncio,我在一个线程类中有一个websocket,它调用on_message函数send_conf上的一个方法 但是在并行线程上有一个方法也会每隔10秒调用同一个函数 我尝试在asyncio中使用run_in_executor,但收到的消息是,事件循环已经在运行 错误 在不阻塞的情况下,从websocket.on_消息和并行线程同时调用send_conf函数的最佳方法是什么 import websocket, threading, json, base64 import time, requests,

我在一个线程类中有一个websocket,它调用on_message函数send_conf上的一个方法

但是在并行线程上有一个方法也会每隔10秒调用同一个函数

我尝试在asyncio中使用run_in_executor,但收到的消息是,事件循环已经在运行 错误

在不阻塞的情况下,从websocket.on_消息和并行线程同时调用send_conf函数的最佳方法是什么

import websocket, threading, json, base64
import time, requests, threading, asyncio
from concurrent.futures import ThreadPoolExecutor

class Count(threading.Thread):

    def __init__(self, apiKey=None, apiSecret=None, curl=None, wsURL=None):
        threading.Thread.__init__(self)
        self.loop = asyncio.get_event_loop()
        self.apiSecret = apiSecret
        self.apiKey = apiKey
        self.curl = curl
        self.wsURL = wsURL
        self.executor = ThreadPoolExecutor(5)

        self.session = requests.Session()

        self.session.headers.update({'user-agent': 'Stacked'})
        self.session.headers.update({'content-type': 'application/json'})
        self.session.headers.update({'accept': 'application/json'})


        self.confirmation = send_conf(0)


    def curl(self, path, query=None, postdict=None, method=None):
        loop = asyncio.get_event_loop()
        return loop.run_until_complete(self.async_curl(path, query, postdict, method))


    async def async_curl(self, path, query=None, postdict=None, method=None):
        loop = asyncio.get_event_loop()
        URL = self.curl + '/api/v1/' + path
        req = requests.Request(method, URL, json=postdict, params=query)
        prepped = self.session.prepare_request(req)
        def do_prepped():
            return self.session.send(prepped, timeout=20)
        response = await loop.run_in_executor(self.executor, do_prepped)
        return response




    def send_conf(self, param):
        METHOD = 'POST'
        LINK = 'conf'

        return self.curl(LINK, postdict=param, method=METHOD)



    def active_patching(self, time_period):
        while self.ws.keep_running:
            x = 2 + 2

            self.send_conf(x)

            time.sleep(time_period)


    def run(self):

        def on_message(ws, message):
            if len(message) > 10:
                self.send_conf(message['stat'])

        def on_error(ws, error):
            print(error)

        def on_close(ws):
            print("### closed ###")
            exit()

        def on_open(ws):
            args = []
            # args.append()
            args.append("activity")
            request = {"operation": "subscribe", "args": args}
            self.ws.send(json.dumps(request))
            print(request)
            self.acpt = threading.Thread(target=lambda: self.active_patching(10))
            self.acpt.daemon = True
            self.acpt.start()

        def exit():
            self.exited = True
            self.ws.close()


        self.loop = asyncio.new_event_loop()
        asyncio.set_event_loop(self.loop)

        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp(self.wsURL,
                                  on_message = on_message,
                                  on_error = on_error,
                                  on_close = on_close,
                                  on_open = on_open)

        self.ws.keep_running = True
        self.ws.run_forever(ping_interval=30, ping_timeout=10)
        self.ws.keep_running = False

让我们从定义调用请求的普通同步方法开始:

显然,不能从asyncio回调或协同路由调用这两个函数,因为它们会阻塞事件循环。要从asyncio和相关代码(如websockets回调)安全地调用此类同步函数,请使用loop.run_in_executor:

在幕后,asyncio将把send_conf提交到一个线程池,挂起您的协同程序,并在send_conf在单独的线程中运行时继续为其他协同程序提供服务。当send_conf完成时,您的协同程序将被唤醒,并得到响应

另一方面,如果您想从另一个线程调用curl或send_conf,只需调用它们即可。是的,它们将阻止该特定线程,直到它们完成,但这不会影响asyncio事件循环

解决了这个问题后,剩下的部分将讨论如何改进体系结构,这样您就根本不需要其他线程了

要从asyncio线程内部(例如从websockets回调)启动async def协程,您不需要运行_直到_完成-事实上,如果您尝试使用它,您将从问题中得到已在运行的事件循环错误。相反,您只需要调用loop.create\u taskself.some\u corroutine

这意味着您不需要继承threading.Thread或生成自己的线程,而只需要在后台运行某些东西。如上所示,asyncio已经允许您使用异步函数编写代码,这些函数看起来像是在单独线程中运行的顺序代码,但没有多线程的缺陷。例如,实现主动修补的惯用方法是使用协同程序:

async def active_patching(self, time_period):
    loop = asyncio.get_event_loop()
    while self.ws.keep_running:
        x = 2 + 2
        await loop.run_in_executor(None, self.send_conf, x)
        await asyncio.sleep(time_period)

def on_open(ws):
    # ...
    loop = asyncio.get_event_loop()
    loop.create_task(self.active_patching(10))
线程的剩余使用隐藏在调用run_in_executor之后,这允许您轻松地对请求进行阻塞调用,但不会干扰asyncio事件循环。而且,如果您采用了一个http库,该库具有对异步IO的本机支持,例如,您根本不需要在执行器中运行

async def some_coroutine(self):
    loop = asyncio.get_event_loop()
    resp = await loop.run_in_executor(None, self.send_conf, param)
async def active_patching(self, time_period):
    loop = asyncio.get_event_loop()
    while self.ws.keep_running:
        x = 2 + 2
        await loop.run_in_executor(None, self.send_conf, x)
        await asyncio.sleep(time_period)

def on_open(ws):
    # ...
    loop = asyncio.get_event_loop()
    loop.create_task(self.active_patching(10))