【问题标题】:Timeout and Windows Services for Long Running Processes (Python)长时间运行进程的超时和 Windows 服务 (Python)
【发布时间】:2012-02-28 12:41:32
【问题描述】:

我有一个使用 python 创建的简单 Windows 服务。我的问题是我不知道服务需要多长时间才能完成,它可能需要 15 秒,或者可能需要 4 个多小时,具体取决于需要对数据执行的操作。 4 小时以上的情况很少见,但我遇到过这种情况。

以下是我一直遵循的 Windows 服务的一般模式。我取出了所有逻辑,但这不是问题,只留下了一个虚拟的日志记录命令。有没有办法阻止服务继续或不刷新,直到逻辑部分完成而不是使用超时?

import win32service
import win32serviceutil
import win32api
import win32con
import win32event
import win32evtlogutil
import os
import sys
import time
import logging
class aservice(win32serviceutil.ServiceFramework):
    _svc_name_ = "WeatherService"
    _svc_display_name_ = "Weather Service"
    _svc_description_ = "Downloads weather data from NOAA and creates maps"
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)           
    def SvcStop(self):
        self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING)
        win32event.SetEvent(self.hWaitStop)                      
    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
        self.timeout = 640000    #640 seconds / 10 minutes (value is in milliseconds)
        #self.timeout = 120000     #120 seconds / 2 minutes
        # This is how long the service will wait to run / refresh itself (see script below)
        while 1:
            # Wait for service stop signal, if I timeout, loop again
            rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout)
            # Check to see if self.hWaitStop happened
            if rc == win32event.WAIT_OBJECT_0:
                # Stop signal encountered
                servicemanager.LogInfoMsg(self._svc_name_ + " - STOPPED!")  #For Event Log
                break
            else:
                #[actual service code between rests]
                try:
                    logging.basicConfig(filename=r"c:\temp\example.log",level=logging.DEBUG,
                                        format='%(asctime)s %(message)s')
                    logging.debug('This message should go to the log file')
                    logging.info('So should this')
                    logging.warning('And this, too')

                    #file_path = "C:\whereever\my_REAL_py_work_to_be_done.py"
                    #execfile(file_path)             #Execute the script
                    #inc_file_path2 = "C:\whereever\MORE_REAL_py_work_to_be_done.py"
                    #execfile(inc_file_path2)        #Execute the script
                except:
                    pass
                #[actual service code between rests]


def ctrlHandler(ctrlType):
    return True

if __name__ == '__main__':   
    win32api.SetConsoleCtrlHandler(ctrlHandler, True)   
    win32serviceutil.HandleCommandLine(aservice)

【问题讨论】:

    标签: python time windows-services


    【解决方案1】:

    您可以启动一个新流程来执行长期运行的事情。如果停止信号到达,则终止子进程。

    【讨论】:

    • 当我将超时设置为无限时,它永远不会超过 rs = win32event.WaitForSingleObject()。那么我应该如何创建停止信号/循环以允许花费无限时间?
    【解决方案2】:

    将服务用作具有多个线程的控制器。 一个线程(主线程)应该同步和排队命令,并使用 win32 服务框架进行通信、注册到系统等。 另一个线程(Worker)应该等待来自队列的命令并执行它们。如果您将任意代码作为单独的进程执行,那么您可以从工作线程中生成这些代码,并在完成后简单地读取结果并进行清理。

    这样,当停止到达时,您的主线程会将其注册到其队列中的工作人员,该工作人员将唤醒并尝试向其他进程发出信号以退出,稍等并清理或强制终止它们.

    更新:

    下面是一个示例概念,说明如何拥有始终响应并在必要时运行的服务。每个工人都可以扔

    ...
    import threading
    ...
    
    class InterruptedException(Exception):
        pass
    
    class WorkerThread(threading.Thread):
        def __init__(self, controller):
            self._controller = controller
            self._stop = threading.Event()
            super(WorkerThread, self).__init__()
    
        def stop(self):
            self._stop.set()
    
        def stopped(self):
            return self._stop.isSet()
    
        def run(self):
            try:
               # Insert the code you want to run as a service here
               # rather than do "execfile(.../.../blah)" simply do:
               # You can have your code throw InterruptedException if your code needs to exit
               # Also check often if self.stopped and then cleanly exit
    
               import your_file
               your_file.main()
    
               # if code in another module is not yours or cannot check often if it should stop then use multiprocessing which will spawn separate processes that you can terminate then from here when you need to stop and return
               # in that case simply block here on self._stop.wait()
    
            except InterruptedException as exc:
               # We are forcefully quitting 
               pass
            except Exception as e:
               # Oh oh, did not anticipate this, better report to Windows or log it
            finally:
               # Close/release any connections, handles, files etc.
    
               # OK, we can stop now
               win32event.SetEvent(self._controller)
    
    def __init__(self, args):
        win32serviceutil.ServiceFramework.__init__(self, args)
        self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)      
        self.hWaitDone = win32event.CreateEvent(None, 0, 0, None)
    
    def SvcDoRun(self):
        import servicemanager      
        servicemanager.LogMsg(servicemanager.EVENTLOG_INFORMATION_TYPE,
                              servicemanager.PYS_SERVICE_STARTED,(self._svc_name_, '')) 
    
        worker = WorkerThread(self.hWaitDone)
        worker.start()
    
        while True:
            # Wait for service stop signal
            rc = win32event.WaitForMultipleObjects([self.hWaitStop, self.hWaitDone], win32event.INFINITE)
    
            # Check to see if self.hWaitStop happened as part of Windows Service Management
            if rc == 0:
                # Stop signal encountered
                servicemanager.LogInfoMsg(self._svc_name_ + " - STOPPED!")  #For Event Log
                break
    
            if rc == 1:
                # Wait until worker has fully finished
                worker.join()
    
                # Determine from worker state if we need to start again (because run finished)
                # Or do whatever
                if not worker.need_to_start_again():
                    break
    
                worker.start()
    

    【讨论】:

    • 我理解停止线程等的概念,但如果我使用这个:rc = win32event.WaitForSingleObject(self.hWaitStop, self.timeout) 其中 self.timeout 是以秒为单位的某个值,我发现服务有时会超时,但我不希望它。我不知道总处理时间。如果我从 self.timeout 更改为 win32event.INFINITE 它也不起作用
    • 请参阅上面的更新,了解如何进行重组以消除对超时的依赖。
    • 服务似乎永远不会超出这里: # 等待服务停止信号 rc = win32event.WaitForMultipleObjects([self.hWaitStop, self.hWaitDone], win32event.INFINITE)。当我查看该服务时,即使是在执行最基本的操作(例如写入文件)时,它也会崩溃。
    【解决方案3】:

    我最终使用了这种模式:http://code.activestate.com/recipes/551780/

    效果很好,但不像魅力。我确实遇到了多处理问题,该过程没有产生实例。对此有何建议?

    请继续发表你的答案,我想看看大家的解决方案,因为 win32api 可能很难使用。

    谢谢大家

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-14
      • 2011-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-04
      相关资源
      最近更新 更多