如何获得相当于使用系统正常运行时间的Python threading.Timer?

时间:2018-02-14 07:49:04

标签: python timer uptime

TL; DR threading.Timer使用系统时间但是时间在我使用时发生变化,如何才能使用系统正常运行时间?

我有一个Python脚本可以执行一系列操作,其中一个设置了系统时间。当这个脚本启动时,时间是错误的。此脚本还需要具有30秒的全局超时。

我一直在使用以下超时类:

class Timeout(object):
    def __init__(self, seconds=1, signum=signal.SIGUSR1, exception=TimeoutException):
        self.exception = exception
        self.pid = os.getpid()
        self.signum = signum
        self.timer = threading.Timer(seconds, self.exit_function)

    def exit_function(self):
        os.kill(self.pid, self.signum)

    def handle_timeout(self, signum, frame):
        raise self.exception()

    def __enter__(self):
        signal.signal(self.signum, self.handle_timeout)
        self.timer.start()

    def __exit__(self, type, value, traceback):
        self.timer.cancel()

包装了我的整个脚本:

with Timeout(seconds=30):
    main()

偶尔脚本会在30秒后很快失败或永远不会被杀死。我相信这是因为threading.Timer使用了在脚本运行时更改的系统时间。无论如何我可以使用系统正常运行时间吗?

2 个答案:

答案 0 :(得分:0)

好像你正在使用Python< 3.3。在Python 3.3或更高版本中,monotonic将是标准库中time.monotonic的别名。 time.monotonic随后也用于threading - 图书馆。如docs中所述:

  

返回单调时钟的值(以小数秒为单位),即不能倒退的时钟。 时钟不受系统时钟更新的影响。

因此,使用Python> = 3.3 threading.Timer将是独立的。

试图解决您的问题: 的视窗

我看到使用kernel32.dll ctypes import ctypes kernel_32 = ctypes.cdll.LoadLibrary("Kernel32.dll") kernel_32.GetTickCount() # overflow after 49.7 days kernel_32.GetTickCount64() 使用gettickcount获取所需系统正常运行时间的选项:

def sleep(time):
    start = kernel_32.GetTickCount64()
    end = start + time
    while kernel_32.GetTickCount64() < end:
        pass
    print('done')

有了这个你就可以创建自己的计时器,其中包含一些不可信的东西:

try:
    clock_gettime = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True).clock_gettime
except Exception:
    clock_gettime = ctypes.CDLL(ctypes.util.find_library('rt'), use_errno=True).clock_gettime

class timespec(ctypes.Structure):
    """Time specification, as described in clock_gettime(3)."""
    _fields_ = (('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long))

if sys.platform.startswith('linux'):
    CLOCK_MONOTONIC = 1
elif sys.platform.startswith('freebsd'):
    CLOCK_MONOTONIC = 4
elif sys.platform.startswith('sunos5'):
    CLOCK_MONOTONIC = 4
elif 'bsd' in sys.platform:
    CLOCK_MONOTONIC = 3
elif sys.platform.startswith('aix'):
    CLOCK_MONOTONIC = ctypes.c_longlong(10)

def monotonic():
    """Monotonic clock, cannot go backward."""
    ts = timespec()
    if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(ts)):
        errno = ctypes.get_errno()
        raise OSError(errno, os.strerror(errno))
    return ts.tv_sec + ts.tv_nsec / 1.0e9

我真的希望这种方法有助于你的问题 - 祝你好运

编辑: 的的Linux

基于monotonic:您可以尝试将其替换为getTickCount 1

"body-parser": "^1.15.2",
"express": "^4.14.0",
"oauth2-server": "^2.4.1",
"request": "^2.83.0"

1:版权所有2014,2015,2016 Ori Livneh   根据Apache许可证2.0版(“许可证”)获得许可;   除非符合许可,否则您不得使用此文件。   您可以在以下位置获取许可证副本   http://www.apache.org/licenses/LICENSE-2.0   除非适用法律要求或书面同意,否则软件   根据许可证分发的“按现状”分发,   不附带任何明示或暗示的保证或条件。

答案 1 :(得分:0)

我最终扩展threading.Timer以使用系统正常运行时间。

class Timer(threading._Timer):

    def __init__(self, *args, **kwargs):
        super(Timer, self).__init__(*args, **kwargs)

        # only works on Linux
        self._libc = ctypes.CDLL('libc.so.6')
        self._buf = ctypes.create_string_buffer(128)

    def uptime(self):
        self._libc.sysinfo(self._buf)
        return struct.unpack_from('@l', self._buf.raw)[0]

    def run(self):
        start_time = self.uptime()
        while not self.finished.is_set():
            time.sleep(0.1)
            if self.uptime() - start_time > self.interval:
                self.function(*self.args, **self.kwargs)
                break
        self.finished.set()