代码之家  ›  专栏  ›  技术社区  ›  Ross MacArthur

如何获得Python线程的等价物。使用系统正常运行时间的计时器?

  •  0
  • Ross MacArthur  · 技术社区  · 8 年前

    TL;博士 穿线。计时器使用系统时间,但在我使用它时时间会发生变化,如何让它使用系统正常运行时间?

    我有一个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 回复  |  直到 8 年前
        1
  •  3
  •   Ross MacArthur    6 年前

    使现代化

    我现在要做的是给猴子打补丁 threading._time 从 monotonic PyPI上的包。

    import threading
    import monotonic
    
    threading._time = monotonic.monotonic
    

    原始答案

    我最终扩展了 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()
    
        2
  •  0
  •   Skandix Bruno Barral    8 年前

    您似乎正在使用Python<3.3. 在Python 3.3或更新版本上, monotonic 将是的别名 time.monotonic 来自标准库。 时间单调的 然后还用于 threading -图书馆。如中所述 docs :

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

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


    尝试解决您的问题: 窗户

    我看到了一个选项,可以使用 gettickcount 从 kernel32.dll 使用 ctypes :

    import ctypes
    kernel_32 = ctypes.cdll.LoadLibrary("Kernel32.dll")
    kernel_32.GetTickCount()  # overflow after 49.7 days
    kernel_32.GetTickCount64()
    

    使用此功能,您可以创建自己的计时器,其中包含以下性能不佳的内容:

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

    我真的希望这种方法能帮助你解决问题-祝你好运


    编辑: Linux操作系统

    基于 monotonic :您可以尝试将此作为getTickCount的替代方法 1. :

    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
    

    1: 版权所有2014、2015、2016 Ori Livneh 根据Apache许可证2.0版(“许可证”)进行许可; 除非遵守许可证,否则不得使用此文件。 您可以通过以下方式获得许可证副本: http://www.apache.org/licenses/LICENSE-2.0 除非适用法律要求或书面同意,软件 根据许可证进行的分发按“原样”进行分发, 无任何明示或暗示的保证或条件。

    推荐文章