第 18.18 節

std::chrono

0瀏覽次數0訪問次數--跳出率--平均停留

本節解決什麼問題

在 C 語言中,處理時間用 time()clock()sleep()gettimeofday() 等函數,它們使用不同的單位和類型(秒、毫秒、微秒...),容易搞混,精度也不夠。

std::chrono 是 C++11 引入的類型安全的時間庫,把"時間點"、"時長"、"時鐘"概念清晰地分開,單位在類型系統中自動管理,不需要手動換算。

這個特性是什麼

std::chrono 提供了三個核心概念:

  • 時長 duration:一段時間長度,如 5 秒、10 毫秒。
  • 時間點 time_point:某個時刻,如 2026-06-02 12:00:00。
  • 時鐘 clock:獲取當前時間的工具,如 system_clocksteady_clock

C++ 標準版本

C++11(基礎),C++20 增加了日曆、時區等。

需要的頭文件

#include <chrono>
#include <thread>  // for std::this_thread::sleep_for

基本語法

using namespace std::chrono;

// 时长
auto t1 = 5s;           // 5 秒
auto t2 = 100ms;        // 100 毫秒
auto t3 = 50us;         // 50 微秒
auto t4 = 10ns;         // 10 纳秒
auto t5 = 1min;         // 1 分钟(C++20)
auto t6 = 2h;           // 2 小时(C++20)

// 时间点
auto now = std::chrono::system_clock::now();  // 当前时间(系统时钟)
auto now2 = std::chrono::steady_clock::now(); // 单调时钟(不会往回跳)

// 时长转换
auto sec = std::chrono::duration_cast<std::chrono::seconds>(100ms);  // 0s(截断)

// 休眠
std::this_thread::sleep_for(500ms);

常用時鐘對比

時鐘特點用途
system_clock系統時間,可轉換為日曆時間顯示時間,時間戳
steady_clock單調遞增,不會往回跳測量耗時(最常用)
high_resolution_clock最高精度時鐘需要最高精度的計時

示例代碼

示例 1:測量一段代碼的執行時間

#include <iostream>
#include <chrono>
#include <thread>  // for sleep

int main()
{
    using namespace std::chrono;

    // 记录开始时间
    auto start = steady_clock::now();

    // 模拟耗时操作
    std::this_thread::sleep_for(500ms);

    // 记录结束时间
    auto end = steady_clock::now();

    // 计算耗时
    auto elapsed = end - start;
    auto elapsed_ms = duration_cast<milliseconds>(elapsed);
    auto elapsed_us = duration_cast<microseconds>(elapsed);

    std::cout << "Elapsed: " << elapsed_ms.count() << " ms\n";
    std::cout << "Elapsed: " << elapsed_us.count() << " us\n";

    return 0;
}

運行結果(大約):

Elapsed: 500 ms
Elapsed: 500000 us

示例 2:在示例 1 基礎上,不同時間單位的使用和轉換

#include <iostream>
#include <chrono>

int main()
{
    using namespace std::chrono;

    // 不同单位
    auto s = 1s;         // 1 秒
    auto ms = 100ms;     // 100 毫秒
    auto us = 500us;     // 500 微秒

    // 时长可以相加
    auto total = s + ms + us;
    std::cout << "total in us: " << duration_cast<microseconds>(total).count() << " us\n";

    // 秒到毫秒的转换
    auto two_seconds = 2s;
    auto as_ms = duration_cast<milliseconds>(two_seconds);
    std::cout << "2s = " << as_ms.count() << " ms\n";

    // 毫秒到秒的转换(会截断)
    auto ms_to_sec = duration_cast<seconds>(1500ms);
    std::cout << "1500ms = " << ms_to_sec.count() << " s\n";  // 1 秒

    return 0;
}

運行結果

total in us: 1100500 us
2s = 2000 ms
1500ms = 1 s

示例 3:在示例 2 基礎上,實現一個簡單的計時器

#include <iostream>
#include <chrono>
#include <functional>

// 简单的计时器:测量函数运行时间
double measure(std::function<void()> func)
{
    using namespace std::chrono;

    auto start = steady_clock::now();
    func();
    auto end = steady_clock::now();

    return duration_cast<microseconds>(end - start).count() / 1000.0;  // 返回毫秒
}

void slow_operation()
{
    int sum = 0;
    for (int i = 0; i < 10000000; ++i)
    {
        sum += i;
    }
    std::cout << "sum = " << sum << "\n";
}

void quick_operation()
{
    int sum = 0;
    for (int i = 0; i < 1000; ++i)
    {
        sum += i;
    }
    std::cout << "sum = " << sum << "\n";
}

int main()
{
    std::cout << "slow_op took " << measure(slow_operation) << " ms\n";
    std::cout << "quick_op took " << measure(quick_operation) << " ms\n";

    return 0;
}

運行結果(運行時間因機器而異):

sum = 887459712
slow_op took 12.5 ms
sum = 499500
quick_op took 0.001 ms

示例 4:在示例 3 基礎上,獲取和格式化系統時間

#include <iostream>
#include <chrono>
#include <ctime>
#include <iomanip>

int main()
{
    using namespace std::chrono;

    // 获取当前系统时间
    auto now = system_clock::now();

    // 获取自 epoch(1970-01-01)以来的秒数
    auto epoch_seconds = duration_cast<seconds>(now.time_since_epoch());
    std::cout << "Seconds since epoch: " << epoch_seconds.count() << "\n";

    // 转换为 C 风格的 time_t 打印
    std::time_t now_c = system_clock::to_time_t(now);
    std::cout << "Current time: " << std::ctime(&now_c);

    // 计算未来时间点
    auto future = now + 24h;  // C++20 的 24h 字面量
    // 如果没有 C++20,可以用: auto future = now + hours(24);
    std::time_t future_c = system_clock::to_time_t(future);
    std::cout << "24 hours later: " << std::ctime(&future_c);

    return 0;
}

運行結果(日期因運行時間而異):

Seconds since epoch: 1748870400
Current time: Mon Jun  2 12:00:00 2026
24 hours later: Tue Jun  3 12:00:00 2026

運行結果

見上方每個示例的"運行結果"。

示例中的關鍵語法解釋

示例講了什麼新出現的語法為什麼這樣寫注意事項
示例 1測量代碼耗時steady_clock::now()duration_cast、字面量 500mssteady_clock 單調遞增,適合計時字面量需要 using namespace std::chrono
示例 2時長單位轉換1s100msduration_cast類型自動管理單位,轉換時要顯式 castduration_cast 是截斷,不是四捨五入
示例 3通用計時器measure(function<void()>)包裝成一個工具函數,可重複使用steady_clock 是最佳選擇
示例 4系統時間和格式化system_clock::now()to_time_t()24hsystem_clock 可以轉為日曆時間system_clock 可能被系統時間調整影響,不用於計時

計時和顯示時間不要混用

需求推薦時鐘原因
測量函數耗時steady_clock單調遞增,不受系統時間調整影響
超時判斷steady_clock不會因為系統時間跳變導致超時錯誤
打印當前日期時間system_clock可以轉換成日曆時間
生成日誌時間戳system_clock對人類可讀

一個常見錯誤是用 system_clock 做超時判斷。如果系統時間被 NTP 或用戶手動調回去,程序可能“等很久都不超時”。測耗時和超時都優先用 steady_clock

示例 5:用 steady_clock 做超時判斷

#include <chrono>
#include <iostream>
#include <thread>

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    using namespace std::chrono;

    auto deadline = steady_clock::now() + 300ms;

    while (steady_clock::now() < deadline)
    {
        std::cout << "waiting...\n";
        std::this_thread::sleep_for(100ms);
    }

    std::cout << "timeout\n";

    // 返回 0 表示程序正常结束。
    return 0;
}

可能的運行結果

waiting...
waiting...
waiting...
timeout

這裡的重點不是循環本身,而是超時基準用 steady_clock。這類寫法在定時器、串口讀取超時、網絡等待超時裡非常常見。

常見錯誤

錯誤 1:忘記 using namespace std::chrono 導致字面量不識別

auto t = 500ms;  // ❌ 编译错误!

正確做法:加 using namespace std::chrono; 或寫 std::chrono::milliseconds(500)

錯誤 2:用 system_clock 測量耗時

auto start = system_clock::now();  // ❌ 系统时间可能被人调回去了!

正確做法:測量耗時一定要用 steady_clock

錯誤 3:忘記 .count() 直接輸出 duration

std::cout << duration_cast<milliseconds>(elapsed);  // ❌ 不能直接输出

正確做法:std::cout << elapsed_ms.count() << " ms\n";

錯誤 4:duration_cast 的截斷問題

auto sec = duration_cast<seconds>(1500ms);  // 1 秒!不是 1.5 秒

正確做法:如果需要小數,用 duration<double>duration_cast<milliseconds> 保留精度。

使用建議

  1. 測量耗時用 steady_clock:單調遞增,不受系統時間調整影響。
  2. 顯示時間用 system_clock:可以轉為日曆時間。
  3. 用字面量 1s100ms 而不是直接寫數字:可讀性更好。
  4. duration_cast 是截斷,不是四捨五入:精度會丟失,注意使用場景。
  5. C++20 增加了日曆和時區支持std::chrono::year_month_day 等,更強大。
  6. 超時判斷也用 steady_clock:和測耗時一樣,不受系統時間調整影響。

小結

  • std::chrono 提供類型安全的時間處理:duration(時長)、time_point(時間點)、clock(時鐘)。
  • steady_clock::now() 測量耗時,用 system_clock::now() 獲取系統時間。
  • 字面量 1s100ms50us 讓時間代碼直觀易讀。
  • duration_cast 做單位轉換,.count() 獲取數值。
  • std::this_thread::sleep_for(500ms) 做線程休眠。

工程拓展

在 ROS2 中,rclcpp::Durationrclcpp::Time 的用法和 std::chrono 非常相似。學好了 std::chrono,理解 ROS2 的時間處理就很容易了。在 Boost.Asio 中,定時器也大量使用 std::chrono::duration

音乐页