如何在现有Python程序中集成asyncio SSH服务并实现变量共享
解决方案
你遇到的问题本质是asyncio的事件循环为单线程调度,原来的同步while循环带time.sleep会阻塞整个事件循环,导致SSH服务和主循环无法同时运行,解决方法分两种场景:
场景1:原有主循环可修改为异步逻辑(推荐)
该方案无需引入多线程,全局变量读写无并发安全问题,实现成本最低。
修改步骤
- 将原有同步主循环改造为异步协程,把所有
time.sleep替换为asyncio.sleep,避免阻塞事件循环 - 在SSH服务启动后,将主循环协程通过
loop.create_task提交到事件循环中调度 - 全局变量可直接在
handle_client和主循环中读写,单线程协程调度不会出现竞态问题
修改后完整代码示例:
import asyncio, asyncssh, sys # 原有依赖库正常导入即可 is_open = True return_value = 0 async def handle_client(process): global is_open, return_value process.stdout.write('Enter numbers one per line, or EOF when done:\n') # 注意布尔值需要转字符串才能写入stdout,否则会报错 process.stdout.write(f"当前is_open值:{is_open}\n") total = 0 try: async for line in process.stdin: line = line.rstrip('\n') # 举个修改变量的例子:输入open/close可直接修改全局变量 if line == 'open': is_open = True process.stdout.write("已修改is_open为True\n") continue if line == 'close': is_open = False process.stdout.write("已修改is_open为False\n") continue if line: try: total += int(line) except ValueError: process.stderr.write('Invalid number: %s\n' % line) except asyncssh.BreakReceived: pass process.stdout.write('Total = %s\n' % total) process.exit(0) # 改造后的异步主循环 async def main_loop(): global return_value while True: # 原有业务逻辑直接放在这里即可,所有time.sleep替换为asyncio.sleep print(f"主循环运行中,return_value={return_value}, is_open={is_open}") return_value += 1 await asyncio.sleep(0.1) async def start_server(): await asyncssh.listen('', 8022, server_host_keys=['key'], authorized_client_keys='key.pub', process_factory=handle_client) # 提交主循环协程到事件队列调度 asyncio.create_task(main_loop()) loop = asyncio.get_event_loop() try: loop.run_until_complete(start_server()) except (OSError, asyncssh.Error) as exc: sys.exit('Error starting server: ' + str(exc)) loop.run_forever()
场景2:原有主循环逻辑复杂,无法改造为异步
如果原有主循环包含大量阻塞同步调用、GPIO操作等无法异步改造的逻辑,可以将主循环放到独立线程中运行,此时读写全局变量需要加线程锁避免竞态。
修改要点
- 引入
threading.Lock作为全局变量的读写锁,所有读写全局变量的位置都要先加锁 - 用
threading.Thread启动独立线程运行原有同步主循环
关键代码片段示例:
import threading, time lock = threading.Lock() is_open = True return_value = 0 # handle_client中读写变量的写法 async def handle_client(process): global is_open # 读变量 with lock: current_is_open = is_open process.stdout.write(f"当前is_open值:{current_is_open}\n") # 写变量 with lock: is_open = False # 原有同步主循环不用修改,仅增加锁逻辑即可 def sync_main_loop(): global return_value while True: with lock: print(f"主循环运行中,return_value={return_value}, is_open={is_open}") return_value +=1 time.sleep(0.1) if __name__ == "__main__": # 启动服务前先启动主循环线程 threading.Thread(target=sync_main_loop, daemon=True).start() # 后面原有SSH启动逻辑保持不变
内容的提问来源于stack exchange,提问作者user105939
相关产品推荐
相关产品推荐

