You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:32:30