使用pywin32创建Windows Service时无法调用asyncio函数的问题
解决pywin32 Windows服务中调用asyncio函数的主线程错误
错误原因
Windows服务通过pywin32启动时,SvcDoRun方法运行在非主线程中。Python 3.8的asyncio在初始化默认事件循环时,会调用set_wakeup_fd方法——这个操作仅允许在主线程执行,因此直接在SvcDoRun里调用asyncio.get_event_loop()等方法会触发ValueError: set_wakeup_fd only works in main thread错误。
解决方案
核心思路是为服务线程创建独立的事件循环,避免依赖主线程的默认循环,同时确保服务能响应停止信号并优雅退出。
修改后的完整代码
import socket import win32serviceutil import servicemanager import win32event import win32service import asyncio class Test(win32serviceutil.ServiceFramework): _svc_name_ = 'testService' _svc_display_name_ = 'Test Service' _svc_description_ = 'Test Service Description' @classmethod def parse_command_line(cls): win32serviceutil.HandleCommandLine(cls) def __init__(self, args): win32serviceutil.ServiceFramework.__init__(self, args) self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) socket.setdefaulttimeout(60) def SvcStop(self): self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) win32event.SetEvent(self.hWaitStop) def SvcDoRun(self): try: # 为当前服务线程创建并绑定独立事件循环 loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) # 启动异步服务逻辑 loop.run_until_complete(self._async_service_loop()) servicemanager.LogMsg( servicemanager.EVENTLOG_INFORMATION_TYPE, servicemanager.PYS_SERVICE_STARTED, (self._svc_name_, '') ) except Exception as e: with open('C:/xxx/errorLogs2.txt', 'a') as f: f.write(f"{type(e).__name__}: {str(e)}\n") async def _async_service_loop(self): # 将Windows服务停止事件转换为asyncio可等待对象 stop_event = asyncio.Event() def signal_stop(): stop_event.set() # 注册停止事件回调,确保在事件循环线程中触发停止信号 win32event.RegisterWaitForSingleObject( self.hWaitStop, lambda *args: asyncio.get_event_loop().call_soon_threadsafe(signal_stop), None, win32event.INFINITE, win32event.QS_HASINPUT ) # 这里替换为你的实际异步业务逻辑 try: while not stop_event.is_set(): await asyncio.sleep(1) # 示例:添加业务代码,比如异步请求、定时任务等 finally: # 优雅清理所有异步任务 await asyncio.gather(*asyncio.all_tasks(), return_exceptions=True) if __name__ == '__main__': Test.parse_command_line()
关键修改说明
- 独立事件循环:用
asyncio.new_event_loop()创建专属事件循环,并通过asyncio.set_event_loop(loop)绑定到当前服务线程,避开主线程限制。 - 停止信号适配:将Windows服务的
hWaitStop事件注册为回调,通过call_soon_threadsafe确保在事件循环线程中触发asyncio.Event,让异步逻辑能感知停止请求。 - 优雅退出:在异步逻辑结束前,用
asyncio.gather清理所有未完成的任务,避免服务停止时出现资源泄漏。
内容的提问来源于stack exchange,提问作者Oliver Mohr Bonometti
相关产品推荐
相关产品推荐

