ZetCode

Python time.monotonic 函数

上次修改时间:2025 年 4 月 11 日

本综合指南探讨了 Python 的 time.monotonic 函数,该函数提供了一个单调时钟,用于可靠的时间测量。我们将介绍其在基准测试、性能计时和实际示例中的用法。

基本定义

time.monotonic 函数返回一个浮点数,表示自未指定起始点以来的秒数。 关键特性是它永远不会向后,即使系统时间被调整。

主要特点:保证单调(永不递减),不受系统时钟更改的影响,是测量经过时间的理想选择,并且具有未指定的参考点(只有差异才重要)。 分辨率取决于平台。

time.monotonic 的基本用法

time.monotonic 最简单的用法是测量两点之间经过的时间。 此示例显示了基本计时功能。

basic_monotonic.py
import time

# Get initial monotonic time
start = time.monotonic()

# Simulate some work
time.sleep(1.5)

# Get end time
end = time.monotonic()

# Calculate elapsed time
elapsed = end - start
print(f"Elapsed time: {elapsed:.3f} seconds")

此示例演示了测量经过时间的基本模式。 两个单调时间戳之间的差异给出了可靠的持续时间。

:.3f 格式说明符以毫秒精度显示时间,但实际精度取决于平台。

比较 time.monotonic 和 time.time

此示例将 time.monotonictime.time 进行比较,以显示系统时钟更改如何以不同的方式影响它们。

compare_clocks.py
import time

print("Starting comparison...")
print("Change your system clock during this test to see the difference")

start_mono = time.monotonic()
start_time = time.time()

time.sleep(10)  # Change system clock during this sleep

end_mono = time.monotonic()
end_time = time.time()

print(f"monotonic duration: {end_mono - start_mono:.2f} seconds")
print(f"time.time duration: {end_time - start_time:.2f} seconds")

time.monotonic 将始终显示 ~10 秒,而如果时钟被调整,time.time 可能会显示不正确的持续时间。

这说明了为什么单调时钟对于可靠的计时测量至关重要,尤其是对于长时间运行的进程。

使用 time.monotonic 进行精确基准测试

由于其高分辨率,time.monotonic 非常适合微基准测试。 此示例测量函数的执行时间。

benchmarking.py
import time

def calculate_primes(n):
    primes = []
    for candidate in range(2, n + 1):
        is_prime = True
        for divisor in range(2, int(candidate ** 0.5) + 1):
            if candidate % divisor == 0:
                is_prime = False
                break
        if is_prime:
            primes.append(candidate)
    return primes

# Time the function
start = time.monotonic()
primes = calculate_primes(10000)
end = time.monotonic()

print(f"Found {len(primes)} primes in {end - start:.6f} seconds")

此模式对于性能优化很有用。 单调时钟提供可靠的计时,不受系统时钟调整的影响。

为了获得更高的精度,请考虑 time.perf_counter,它可能在某些平台上提供更好的分辨率。

创建计时器类

此示例显示了如何使用 time.monotonic 创建可重用的计时器类以进行精确的计时测量。

timer_class.py
import time

class Timer:
    def __init__(self):
        self._start = None
        self._elapsed = 0
    
    def start(self):
        if self._start is not None:
            raise RuntimeError("Timer already running")
        self._start = time.monotonic()
    
    def stop(self):
        if self._start is None:
            raise RuntimeError("Timer not running")
        self._elapsed += time.monotonic() - self._start
        self._start = None
    
    def reset(self):
        self._start = None
        self._elapsed = 0
    
    @property
    def elapsed(self):
        if self._start is not None:
            return self._elapsed + (time.monotonic() - self._start)
        return self._elapsed

# Usage example
timer = Timer()
timer.start()
time.sleep(1.5)
timer.stop()
print(f"Elapsed: {timer.elapsed:.3f} seconds")

timer.start()
time.sleep(0.75)
timer.stop()
print(f"Total elapsed: {timer.elapsed:.3f} seconds")

计时器类可以启动、停止和查询经过的时间。 它累积多个启动/停止周期的时间。

这对于分析需要在多个执行中进行累积计时的代码段很有用。

使用 time.monotonic 实现超时

此示例显示了如何使用 time.monotonic 实现超时机制,该机制不会受到系统时钟更改的影响。

timeout.py
import time

def run_with_timeout(timeout_seconds, func, *args, **kwargs):
    start = time.monotonic()
    result = func(*args, **kwargs)
    elapsed = time.monotonic() - start
    
    if elapsed > timeout_seconds:
        raise TimeoutError(f"Operation timed out after {elapsed:.2f} seconds")
    return result

# Example usage
def long_running_operation(duration):
    time.sleep(duration)
    return "Done"

try:
    # This will succeed
    print(run_with_timeout(2, long_running_operation, 1.5))
    
    # This will timeout
    print(run_with_timeout(1, long_running_operation, 1.5))
except TimeoutError as e:
    print(f"Error: {e}")

超时是可靠的,因为它使用单调时钟。 即使在执行期间系统时钟发生变化,超时也能正常工作。

请注意,这实际上不会中断该函数 - 它只会在完成后检查时间。 对于真正的中断,请考虑使用线程。

测量游戏循环中的帧速率

此示例演示了如何使用 time.monotonic 来测量和保持游戏循环中一致的帧速率。

game_loop.py
import time

TARGET_FPS = 60
FRAME_TIME = 1.0 / TARGET_FPS

def game_loop():
    frame_count = 0
    start_time = time.monotonic()
    last_frame_time = start_time
    
    while frame_count < 120:  # Run for 120 frames
        current_time = time.monotonic()
        elapsed = current_time - last_frame_time
        
        # Only process frame if enough time has passed
        if elapsed >= FRAME_TIME:
            # Simulate game logic and rendering
            time.sleep(0.005)  # Simulate variable work time
            
            frame_count += 1
            last_frame_time = current_time
            
            # Calculate current FPS
            total_elapsed = current_time - start_time
            current_fps = frame_count / total_elapsed
            print(f"Frame {frame_count}: {current_fps:.1f} FPS")

game_loop()

游戏循环使用单调时钟来保持一致的计时,而不管系统时钟如何变化。 它计算实际达到的 FPS。

这种模式对于游戏和模拟至关重要,在这些游戏和模拟中,一致的计时比挂钟时间更重要。

最佳实践

资料来源

作者

我的名字是 Jan Bodnar,我是一位热情的程序员,拥有丰富的编程经验。 我自 2007 年以来一直在撰写编程文章。到目前为止,我已经撰写了超过 1,400 篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程