当前位置: 首页 > article >正文

C++编程:实现简单的高精度时间日志记录小程序

0. 概述

为了检查是否存在系统时间跳变,本文使用C++实现了一个简单的高精度时间日志记录小程序。该程序能够每隔指定时间(默认40毫秒)记录一次系统时间到文件中,并具备以下功能:

  1. 自定义时间间隔和文件名:通过命令行参数设置时间间隔和输出文件名,默认值为40毫秒和timestamps.txt
  2. 高精度定时:采用std::chrono::steady_clocksleep_until确保时间间隔的准确性,避免std::this_thread::sleep_for带来的时间漂移。
  3. 缓存队列:使用线程安全的队列缓存时间戳,减少频繁的文件I/O操作。
  4. 日志轮转:当日志文件大小超过100MB时,自动进行日志轮转。
  5. 时间跳变检测:使用[TIME_JUMP]标记日志中发生的时间跳变(即后续时间戳小于前一个时间戳的情况)。
    默认仅记录跳变前后的10个时间戳,避免日志文件中充斥过多正常的时间记录,突出异常事件。

1. 代码实现

超过100MB
未超过
开始
初始化参数
启动生产者线程
启动消费者线程
获取当前时间
推入队列
计算下一个时间点
休眠至下一个时间点
继续运行?
结束生产者线程
等待1秒
获取所有时间戳
检测时间跳变
写入日志文件
检查文件大小
进行日志轮转
继续循环
等待消费者线程结束
程序结束

1.1. 线程安全队列(ThreadSafeQueue)

实现了一个简单的线程安全队列类ThreadSafeQueue,使用std::mutexstd::condition_variable来确保并发访问的安全性。

// Thread-safe queue for storing timestamps
class ThreadSafeQueue {
public:
    // Push a new timestamp into the queue
    void push(const std::string& value) {
        std::lock_guard<std::mutex> lock(mtx_);
        queue_.push(value);
        cv_.notify_one();
    }

    // Retrieve and clear all timestamps from the queue
    std::queue<std::string> popAll() {
        std::lock_guard<std::mutex> lock(mtx_);
        std::queue<std::string> temp;
        std::swap(temp, queue_);
        return temp;
    }

private:
    std::queue<std::string> queue_;
    std::mutex mtx_;
    std::condition_variable cv_;
};

1.2. 获取当前时间的字符串表示

使用gettimeofday获取系统当前时间,并将其格式化为字符串,精确到微秒。

// Function to get the current time as a formatted string
std::string getCurrentTimeString() {
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    struct tm* tm_info = localtime(&tv.tv_sec);

    char buffer[64];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // Append microseconds
    char currentTime[100];
    snprintf(currentTime, sizeof(currentTime), "%s.%06ld", buffer, tv.tv_usec);
    return std::string(currentTime);
}

1.3. 文件大小获取

使用stat函数获取指定文件的大小,以便在日志轮转时进行判断。

// Function to get the size of a file in bytes
std::size_t getFileSize(const std::string& filename) {
    struct stat stat_buf;
    int rc = stat(filename.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : 0;
}

1.4. 时间戳解析

将时间戳字符串解析为自纪元以来的微秒数,方便进行时间比较以检测时间跳变。

// Function to parse timestamp string to microseconds since epoch
uint64_t parseTimestamp(const std::string& timestamp_str) {
    struct tm tm_info;
    int microseconds = 0;

    // Split the timestamp into datetime and microseconds
    size_t dot_pos = timestamp_str.find('.');
    if (dot_pos == std::string::npos) {
        // If no microseconds part, parse as is
        if (strptime(timestamp_str.c_str(), "%Y-%m-%d %H:%M:%S", &tm_info) == nullptr) {
            return 0;
        }
    } else {
        std::string datetime_str = timestamp_str.substr(0, dot_pos);
        std::string micro_str = timestamp_str.substr(dot_pos + 1);
        
        // Parse datetime part
        if (strptime(datetime_str.c_str(), "%Y-%m-%d %H:%M:%S", &tm_info) == nullptr) {
            return 0;
        }
        
        // Parse microseconds part
        try {
            microseconds = std::stoi(micro_str);
        } catch (...) {
            microseconds = 0;
        }
    }

    time_t seconds = mktime(&tm_info);
    if (seconds == -1) {
        return 0;
    }

    uint64_t total_microseconds = static_cast<uint64_t>(seconds) * 1000000 + microseconds;
    return total_microseconds;
}

1.5. 主函数实现

主函数负责解析命令行参数,启动生产者和消费者线程,并控制程序的运行时间和日志轮转。

int main(int argc, char* argv[]) {
    // Default values
    int interval_ms = 40; // Default interval of 40 milliseconds
    std::string filename = "timestamps.txt"; // Default filename
    int run_time_seconds = 3600; // Default run time of 1 hour (3600 seconds)
    const std::size_t max_file_size = 100 * 1024 * 1024; // 100MB

    // Parse command-line arguments
    for(int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if((arg == "-i" || arg == "--interval") && i + 1 < argc) {
            interval_ms = std::atoi(argv[++i]);
            if(interval_ms <= 0) {
                std::cerr << "Invalid interval. Using default value of 40 milliseconds." << std::endl;
                interval_ms = 40;
            }
        }
        else if((arg == "-f" || arg == "--file") && i + 1 < argc) {
            filename = argv[++i];
        }
        else if((arg == "-t" || arg == "--time") && i + 1 < argc) {
            run_time_seconds = std::atoi(argv[++i]);
            if(run_time_seconds <= 0) {
                std::cerr << "Invalid run time. Using default value of 3600 seconds (1 hour)." << std::endl;
                run_time_seconds = 3600;
            }
        }
        else if(arg == "-h" || arg == "--help") {
            std::cout << "Usage: " << argv[0] << " [options]\n"
                      << "Options:\n"
                      << "  -i, --interval <milliseconds>    Set the time interval, default is 40 milliseconds\n"
                      << "  -f, --file <filename>            Set the output filename, default is timestamps.txt\n"
                      << "  -t, --time <seconds>             Set the run time in seconds, default is 3600 seconds (1 hour)\n"
                      << "  -h, --help                       Show this help message\n";
            return 0;
        }
        else {
            std::cerr << "Unknown argument: " << arg << "\nUse -h or --help to see available options." << std::endl;
            return 1;
        }
    }

    std::cout << "Time Interval: " << interval_ms << " milliseconds" << std::endl;
    std::cout << "Output File: " << filename << std::endl;
    std::cout << "Run Time: " << run_time_seconds << " seconds" << std::endl;

    ThreadSafeQueue timeQueue;
    std::atomic<bool> running(true);
    std::atomic<int> file_index(1); // For log rotation

    // Producer thread: collects timestamps at specified intervals
    std::thread producer([&timeQueue, &running, interval_ms]() {
        using clock = std::chrono::steady_clock;
        auto next_time = clock::now();

        while (running.load()) {
            // Get current time and push to queue
            std::string currentTime = getCurrentTimeString();
            timeQueue.push(currentTime);

            // Calculate next time point
            next_time += std::chrono::milliseconds(interval_ms);

            // Sleep until the next time point
            std::this_thread::sleep_until(next_time);

            // Adjust next_time if we're behind schedule
            auto now = clock::now();
            if (now > next_time) {
                next_time = now;
            }
        }
    });

    // Consumer thread: writes timestamps to the file, handles log rotation, and detects time jumps
    std::thread consumer([&timeQueue, &running, &filename, &file_index, max_file_size]() {
        std::ofstream outFile(filename, std::ios::out | std::ios::app);
        if (!outFile.is_open()) {
            std::cerr << "Unable to open file for writing: " << filename << std::endl;
            running.store(false);
            return;
        }

        uint64_t last_timestamp_us = 0; // To store the last timestamp in microseconds

        while (running.load()) {
            // Wait for 1 second before writing to the file
            std::this_thread::sleep_for(std::chrono::seconds(1));

            // Retrieve all timestamps from the queue
            std::queue<std::string> tempQueue = timeQueue.popAll();

            // Write timestamps to the file
            while (!tempQueue.empty()) {
                std::string current_timestamp_str = tempQueue.front();
                tempQueue.pop();

                uint64_t current_timestamp_us = parseTimestamp(current_timestamp_str);
                bool time_jump = false;

                if (last_timestamp_us != 0 && current_timestamp_us < last_timestamp_us) {
                    time_jump = true;
                }

                if (time_jump) {
                    outFile << current_timestamp_str << " [TIME_JUMP]" << std::endl;
                } else {
                    outFile << current_timestamp_str << std::endl;
                }

                last_timestamp_us = current_timestamp_us;
            }

            outFile.flush(); // Ensure data is written to the file

            // Check if file size exceeds the maximum limit
            std::size_t current_size = getFileSize(filename);
            if (current_size >= max_file_size) {
                outFile.close();

                // Generate a new filename with an index
                std::ostringstream new_filename;
                new_filename << filename << "." << file_index++;

                // Rename the current file
                if (std::rename(filename.c_str(), new_filename.str().c_str()) != 0) {
                    std::cerr << "Failed to rotate log file." << std::endl;
                } else {
                    std::cout << "Rotated log file to " << new_filename.str() << std::endl;
                }

                // Open a new file
                outFile.open(filename, std::ios::out | std::ios::app);
                if (!outFile.is_open()) {
                    std::cerr << "Unable to open new file for writing: " << filename << std::endl;
                    running.store(false);
                    return;
                }

                // Reset last_timestamp_us after rotation
                last_timestamp_us = 0;
            }
        }

        // Write any remaining timestamps before exiting
        std::queue<std::string> tempQueue = timeQueue.popAll();
        while (!tempQueue.empty()) {
            std::string current_timestamp_str = tempQueue.front();
            tempQueue.pop();

            uint64_t current_timestamp_us = parseTimestamp(current_timestamp_str);
            bool time_jump = false;

            if (last_timestamp_us != 0 && current_timestamp_us < last_timestamp_us) {
                time_jump = true;
            }

            if (time_jump) {
                outFile << current_timestamp_str << " [TIME_JUMP]" << std::endl;
            } else {
                outFile << current_timestamp_str << std::endl;
            }

            last_timestamp_us = current_timestamp_us;
        }
        outFile.flush();
        outFile.close();
    });

    std::cout << "Program will run for " << run_time_seconds << " seconds and then exit." << std::endl;

    // Let the program run for the specified duration
    std::this_thread::sleep_for(std::chrono::seconds(run_time_seconds));
    running.store(false);

    // Wait for threads to finish
    producer.join();
    consumer.join();

    std::cout << "Program has ended." << std::endl;
    return 0;
}

2. 编译与运行

2.1 编译

g++ -std=c++11 -pthread -o time_logger time_logger.cpp

运行

  • 使用默认设置(40毫秒间隔,输出文件为timestamps.txt,运行1小时):

    ./time_logger
    
  • 自定义设置(例如,100毫秒间隔,输出文件为output.txt,运行2小时):

    ./time_logger -i 100 -f output.txt -t 7200
    

2.2 示例日志文件

以下是日志文件timestamps.txt的一部分示例内容,其中标记了时间跳变:

2024-04-27 12:34:56.789123
2024-04-27 12:34:56.829456
2024-04-27 12:34:56.869789
2024-04-27 12:34:56.809012 [TIME_JUMP]
2024-04-27 12:34:56.849345
...

3. 附完整代码

// g++ -std=c++11 -pthread -o time_logger time_logger.cpp
#include <iostream>
#include <fstream>
#include <queue>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
#include <sys/time.h>
#include <atomic>
#include <string>
#include <cstdlib>
#include <iomanip>
#include <sstream>
#include <sys/stat.h>

// Thread-safe queue for storing timestamps
class ThreadSafeQueue {
public:
    // Push a new timestamp into the queue
    void push(const std::string& value) {
        std::lock_guard<std::mutex> lock(mtx_);
        queue_.push(value);
        cv_.notify_one();
    }

    // Retrieve and clear all timestamps from the queue
    std::queue<std::string> popAll() {
        std::lock_guard<std::mutex> lock(mtx_);
        std::queue<std::string> temp;
        std::swap(temp, queue_);
        return temp;
    }

private:
    std::queue<std::string> queue_;
    std::mutex mtx_;
    std::condition_variable cv_;
};

// Function to get the current time as a formatted string
std::string getCurrentTimeString() {
    struct timeval tv;
    gettimeofday(&tv, nullptr);
    struct tm* tm_info = localtime(&tv.tv_sec);

    char buffer[64];
    strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", tm_info);
    
    // Append microseconds
    char currentTime[100];
    snprintf(currentTime, sizeof(currentTime), "%s.%06ld", buffer, tv.tv_usec);
    return std::string(currentTime);
}

// Function to get the size of a file in bytes
std::size_t getFileSize(const std::string& filename) {
    struct stat stat_buf;
    int rc = stat(filename.c_str(), &stat_buf);
    return rc == 0 ? stat_buf.st_size : 0;
}

// Function to parse timestamp string to microseconds since epoch
uint64_t parseTimestamp(const std::string& timestamp_str) {
    struct tm tm_info;
    int microseconds = 0;

    // Split the timestamp into datetime and microseconds
    size_t dot_pos = timestamp_str.find('.');
    if (dot_pos == std::string::npos) {
        // If no microseconds part, parse as is
        if (strptime(timestamp_str.c_str(), "%Y-%m-%d %H:%M:%S", &tm_info) == nullptr) {
            return 0;
        }
    } else {
        std::string datetime_str = timestamp_str.substr(0, dot_pos);
        std::string micro_str = timestamp_str.substr(dot_pos + 1);
        
        // Parse datetime part
        if (strptime(datetime_str.c_str(), "%Y-%m-%d %H:%M:%S", &tm_info) == nullptr) {
            return 0;
        }
        
        // Parse microseconds part
        try {
            microseconds = std::stoi(micro_str);
        } catch (...) {
            microseconds = 0;
        }
    }

    time_t seconds = mktime(&tm_info);
    if (seconds == -1) {
        return 0;
    }

    uint64_t total_microseconds = static_cast<uint64_t>(seconds) * 1000000 + microseconds;
    return total_microseconds;
}

int main(int argc, char* argv[]) {
    // Default values
    int interval_ms = 40; // Default interval of 40 milliseconds
    std::string filename = "timestamps.txt"; // Default filename
    int run_time_seconds = 3600; // Default run time of 1 hour (3600 seconds)
    const std::size_t max_file_size = 100 * 1024 * 1024; // 100MB
    bool selective_logging = true; // Default: enable selective logging

    // Parse command-line arguments
    for(int i = 1; i < argc; ++i) {
        std::string arg = argv[i];
        if((arg == "-i" || arg == "--interval") && i + 1 < argc) {
            interval_ms = std::atoi(argv[++i]);
            if(interval_ms <= 0) {
                std::cerr << "Invalid interval. Using default value of 40 milliseconds." << std::endl;
                interval_ms = 40;
            }
        }
        else if((arg == "-f" || arg == "--file") && i + 1 < argc) {
            filename = argv[++i];
        }
        else if((arg == "-t" || arg == "--time") && i + 1 < argc) {
            run_time_seconds = std::atoi(argv[++i]);
            if(run_time_seconds <= 0) {
                std::cerr << "Invalid run time. Using default value of 3600 seconds (1 hour)." << std::endl;
                run_time_seconds = 3600;
            }
        }
        else if(arg == "--disable_selective_logging") {
            selective_logging = false;
        }
        else if(arg == "-h" || arg == "--help") {
            std::cout << "Usage: " << argv[0] << " [options]\n"
                      << "Options:\n"
                      << "  -i, --interval <milliseconds>        Set the time interval, default is 40 milliseconds\n"
                      << "  -f, --file <filename>                Set the output filename, default is timestamps.txt\n"
                      << "  -t, --time <seconds>                 Set the run time in seconds, default is 3600 seconds (1 hour)\n"
                      << "      --disable_selective_logging       Disable selective logging feature\n"
                      << "  -h, --help                           Show this help message\n";
            return 0;
        }
        else {
            std::cerr << "Unknown argument: " << arg << "\nUse -h or --help to see available options." << std::endl;
            return 1;
        }
    }

    std::cout << "Time Interval: " << interval_ms << " milliseconds" << std::endl;
    std::cout << "Output File: " << filename << std::endl;
    std::cout << "Run Time: " << run_time_seconds << " seconds" << std::endl;
    std::cout << "Selective Logging: " << (selective_logging ? "Enabled" : "Disabled") << std::endl;

    ThreadSafeQueue timeQueue;
    std::atomic<bool> running(true);
    std::atomic<int> file_index(1); // For log rotation

    // Producer thread: collects timestamps at specified intervals
    std::thread producer([&timeQueue, &running, interval_ms]() {
        using clock = std::chrono::steady_clock;
        auto next_time = clock::now();

        while (running.load()) {
            // Get current time and push to queue
            std::string currentTime = getCurrentTimeString();
            timeQueue.push(currentTime);

            // Calculate next time point
            next_time += std::chrono::milliseconds(interval_ms);

            // Sleep until the next time point
            std::this_thread::sleep_until(next_time);

            // Adjust next_time if we're behind schedule
            auto now = clock::now();
            if (now > next_time) {
                next_time = now;
            }
        }
    });

    // Consumer thread: writes timestamps to the file, handles log rotation, and detects time jumps
    std::thread consumer([&timeQueue, &running, &filename, &file_index, max_file_size, selective_logging]() {
        std::ofstream outFile(filename, std::ios::out | std::ios::app);
        if (!outFile.is_open()) {
            std::cerr << "Unable to open file for writing: " << filename << std::endl;
            running.store(false);
            return;
        }

        uint64_t last_timestamp_us = 0; // To store the last timestamp in microseconds
        std::deque<std::string> recent_timestamps; // To store recent timestamps for buffer
        const size_t buffer_size = 10; // Number of timestamps to keep before a time jump

        size_t normal_count = 0; // Counter for normal timestamps
        const size_t normal_threshold = 20; // Write every 20 normal timestamps

        bool in_jump_mode = false; // Flag indicating if currently in jump mode
        size_t jump_remaining = 0; // Number of jump-related timestamps remaining to write
        std::vector<std::string> jump_buffer; // Buffer to store jump-related timestamps

        while (running.load()) {
            // Wait for 1 second before processing
            std::this_thread::sleep_for(std::chrono::seconds(1));

            // Retrieve all timestamps from the queue
            std::queue<std::string> tempQueue = timeQueue.popAll();

            while (!tempQueue.empty()) {
                std::string current_timestamp_str = tempQueue.front();
                tempQueue.pop();

                // Update recent_timestamps buffer
                recent_timestamps.push_back(current_timestamp_str);
                if (recent_timestamps.size() > buffer_size) {
                    recent_timestamps.pop_front();
                }

                uint64_t current_timestamp_us = parseTimestamp(current_timestamp_str);
                bool time_jump = false;

                if (selective_logging && last_timestamp_us != 0 && current_timestamp_us < last_timestamp_us) {
                    time_jump = true;
                }

                if (selective_logging && time_jump && !in_jump_mode) {
                    // Time jump detected, enter jump mode
                    in_jump_mode = true;
                    jump_remaining = 10; // Number of timestamps to write after the jump
                    jump_buffer.clear();

                    // Add the last 10 timestamps before the jump
                    for (const auto& ts : recent_timestamps) {
                        jump_buffer.push_back(ts);
                    }

                    // Add the current (jump) timestamp
                    jump_buffer.push_back(current_timestamp_str);

                    last_timestamp_us = current_timestamp_us;
                }
                else if (selective_logging && in_jump_mode) {
                    // In jump mode, collect the next 10 timestamps
                    jump_buffer.push_back(current_timestamp_str);
                    jump_remaining--;

                    last_timestamp_us = current_timestamp_us;

                    if (jump_remaining == 0) {
                        // Write all collected jump timestamps to file with [TIME_JUMP] flag
                        for (size_t i = 0; i < jump_buffer.size(); ++i) {
                            if (i == buffer_size) { // The jump timestamp
                                outFile << jump_buffer[i] << " [TIME_JUMP]" << std::endl;
                            }
                            else {
                                outFile << jump_buffer[i] << std::endl;
                            }
                        }
                        outFile.flush();
                        jump_buffer.clear();
                        in_jump_mode = false;
                    }
                }
                else {
                    // Normal timestamp processing
                    normal_count++;
                    if (normal_count >= normal_threshold) {
                        outFile << current_timestamp_str << std::endl;
                        outFile.flush();
                        normal_count = 0;
                    }
                    last_timestamp_us = current_timestamp_us;
                }

                // Check if file size exceeds the maximum limit
                std::size_t current_size = getFileSize(filename);
                if (current_size >= max_file_size) {
                    outFile.close();

                    // Generate a new filename with an index
                    std::ostringstream new_filename;
                    new_filename << filename << "." << file_index++;

                    // Rename the current file
                    if (std::rename(filename.c_str(), new_filename.str().c_str()) != 0) {
                        std::cerr << "Failed to rotate log file." << std::endl;
                    } else {
                        std::cout << "Rotated log file to " << new_filename.str() << std::endl;
                    }

                    // Open a new file
                    outFile.open(filename, std::ios::out | std::ios::app);
                    if (!outFile.is_open()) {
                        std::cerr << "Unable to open new file for writing: " << filename << std::endl;
                        running.store(false);
                        return;
                    }

                    // Reset last_timestamp_us and buffers after rotation
                    last_timestamp_us = 0;
                    recent_timestamps.clear();
                    jump_buffer.clear();
                    in_jump_mode = false;
                    normal_count = 0;
                }
            }
        }

        // Handle any remaining timestamps before exiting
        std::queue<std::string> remainingQueue = timeQueue.popAll();
        while (!remainingQueue.empty()) {
            std::string current_timestamp_str = remainingQueue.front();
            remainingQueue.pop();

            // Update recent_timestamps buffer
            recent_timestamps.push_back(current_timestamp_str);
            if (recent_timestamps.size() > buffer_size) {
                recent_timestamps.pop_front();
            }

            uint64_t current_timestamp_us = parseTimestamp(current_timestamp_str);
            bool time_jump = false;

            if (selective_logging && last_timestamp_us != 0 && current_timestamp_us < last_timestamp_us) {
                time_jump = true;
            }

            if (selective_logging && time_jump && !in_jump_mode) {
                // Time jump detected, enter jump mode
                in_jump_mode = true;
                jump_remaining = 10; // Number of timestamps to write after the jump
                jump_buffer.clear();

                // Add the last 10 timestamps before the jump
                for (const auto& ts : recent_timestamps) {
                    jump_buffer.push_back(ts);
                }

                // Add the current (jump) timestamp
                jump_buffer.push_back(current_timestamp_str);

                last_timestamp_us = current_timestamp_us;
            }
            else if (selective_logging && in_jump_mode) {
                // In jump mode, collect the next 10 timestamps
                jump_buffer.push_back(current_timestamp_str);
                jump_remaining--;

                last_timestamp_us = current_timestamp_us;

                if (jump_remaining == 0) {
                    // Write all collected jump timestamps to file with [TIME_JUMP] flag
                    for (size_t i = 0; i < jump_buffer.size(); ++i) {
                        if (i == buffer_size) { // The jump timestamp
                            outFile << jump_buffer[i] << " [TIME_JUMP]" << std::endl;
                        }
                        else {
                            outFile << jump_buffer[i] << std::endl;
                        }
                    }
                    outFile.flush();
                    jump_buffer.clear();
                    in_jump_mode = false;
                }
            }
            else {
                // Normal timestamp processing
                normal_count++;
                if (normal_count >= normal_threshold) {
                    outFile << current_timestamp_str << std::endl;
                    outFile.flush();
                    normal_count = 0;
                }
                last_timestamp_us = current_timestamp_us;
            }

            // Check if file size exceeds the maximum limit
            std::size_t current_size = getFileSize(filename);
            if (current_size >= max_file_size) {
                outFile.close();

                // Generate a new filename with an index
                std::ostringstream new_filename;
                new_filename << filename << "." << file_index++;

                // Rename the current file
                if (std::rename(filename.c_str(), new_filename.str().c_str()) != 0) {
                    std::cerr << "Failed to rotate log file." << std::endl;
                } else {
                    std::cout << "Rotated log file to " << new_filename.str() << std::endl;
                }

                // Open a new file
                outFile.open(filename, std::ios::out | std::ios::app);
                if (!outFile.is_open()) {
                    std::cerr << "Unable to open new file for writing: " << filename << std::endl;
                    running.store(false);
                    return;
                }

                // Reset last_timestamp_us and buffers after rotation
                last_timestamp_us = 0;
                recent_timestamps.clear();
                jump_buffer.clear();
                in_jump_mode = false;
                normal_count = 0;
            }
        }

        outFile.flush();
        outFile.close();
    });

    std::cout << "Program will run for " << run_time_seconds << " seconds and then exit." << std::endl;

    // Let the program run for the specified duration
    std::this_thread::sleep_for(std::chrono::seconds(run_time_seconds));
    running.store(false);

    // Wait for threads to finish
    producer.join();
    consumer.join();

    std::cout << "Program has ended." << std::endl;
    return 0;
}



http://www.kler.cn/news/323961.html

相关文章:

  • 大厂AI必备数据结构与算法——链表(三)详细文档
  • AI电销机器人是当代电销企业的新宠,智能机器人部署
  • 设计模式之策略设计模式
  • vue仿chatGpt的AI聊天功能--大模型通义千问(阿里云)
  • 鼎跃安全丨多功能气体检测报警系统:工业安全守护者
  • 菱形继承的类对父类的初始化、组合、多态、多态的原理等的介绍
  • C#基础:掌握控制流语句,构建灵活的程序逻辑
  • Python中的“属性与方法”:解锁面向对象编程的秘密
  • 2024年9月25日,Intel发布至强6900P系列:128核心504MB缓存,终于追上AMD!
  • 跨多场景帧重建DENSER:使用小波估计进行城市动态场景重构
  • 机器学习:探索未知边界,解锁智能潜力
  • 华为-单臂路由
  • 服务运营 | 运营前沿:生成式AI改变医疗保健的运作方式
  • SignApp签名工具/美淘iOS在线签名工具/后端PHP
  • MATLAB与Docker Compose:实现微服务API文档的自动化部署与Vue.js集成
  • 算法分类自动驾驶主要算法介绍
  • 三分钟让你掌握PDF转音频:PDF2Audio背后的秘密
  • 2016年国赛高教杯数学建模C题电池剩余放电时间预测解题全过程文档及程序
  • easyexcel常见问题分析
  • html怎么让字体变颜色
  • Android (rust) vulkan (JNI) 画一个三角形: VulkanSurfaceView 初始化
  • ceph rgw 桶分片之reshard
  • 华为GaussDB数据库之Yukon安装与使用
  • 自動獲取IP地址和寬頻撥號上網的詳細指南
  • 828华为云征文|部署个人知识管理系统 SiyuanNote
  • Linux下C开发使用小技巧
  • _RET_IP_ 和_THIS_IP_ 作用
  • cesium的学习过程和使用案例
  • 闲盒支持的组网方式和注意事项
  • gitlab使用小结