繁体   English   中英

C ++中的线程安全日志缓冲区?

[英]Thread-safe log buffer in C++?

我出于性能目的而实现自己的日志记录系统(并且因为我基本上只需要一个缓冲区)。 我目前拥有的是这样的:

// category => messages
static std::unordered_map<std::string, std::ostringstream> log;

void main() {
    while (true) {
        log["info"] << "Whatever";
        log["192.168.0.1"] << "This is a dynamic entry";

        dump_logs();
    }
}

void dump_logs() {
    // i do something like this for each category, but they have different save strategies
    if (log["info"].tellp() > 1000000) {
        // save the ostringstream to a file

        // clear the log
        log["info"].str("")
    }
}

它运作完美。 但是,我刚刚添加了线程,并且不确定此代码是否是线程安全的。 有小费吗?

您可以通过声明地图 thread_local来确保此线程安全。 如果要跨翻译单元使用它,请使其extern并在一个 翻译单元中定义它,否则可以使用static

您仍然需要同步将日志写入磁盘。 互斥锁应解决以下问题:

// category => messages (one per thread)
thread_local static std::unordered_map<std::string, std::ostringstream> log;

void main() {
    while (true) {
        log["info"] << "Whatever";
        log["192.168.0.1"] << "This is a dynamic entry";

        dump_logs();
    }
}

void dump_logs() {

    static std::mutex mtx; // mutex shared between threads

    // i do something like this for each category, but they have different save strategies
    if (log["info"].tellp() > 1000000) {

        // now I need to care about threads
        // use { to create a lock that will release at the end }
        {
            std::lock_guard<std::mutex> lock(mtx); // synchronized access

            // save the ostringstream to a file
        }

        // clear the log
        log["info"].str("");
    }
}

在POSIX系统上,如果您总是将数据写入文件末尾,则多个线程将数据写入文件的最快方法是在附加模式下使用低级C风格的open() ,然后调用write() ,因为对POSIX标准write()规定:

在常规文件或其他能够查找的文件上,实际的数据写入应从文件中与fildes关联的文件偏移所指示的位置开始。 从write()成功返回之前,文件偏移量应增加实际写入的字节数。 在普通文件上,如果最后写入的字节的位置大于或等于文件的长度,则文件的长度应设置为此位置加一。

...

如果设置了文件状态标志的O_APPEND标志,则应在每次写操作之前将文件偏移量设置到文件的末尾,并且在更改文件偏移量和写操作之间不得进行任何中间文件修改操作。

因此,从进程内到以append模式打开的文件的所有write()调用都是原子的。

不需要互斥。

几乎。 您唯一需要关注的问题是

如果write()在成功写入某些数据后被信号中断,则它将返回写入的字节数。

如果您对环境有足够的控制权,可以确保仅写入部分数据后不会中断信号对write()调用,这是将数据从多个文件写入文件的最快方法线程-您正在使用操作系统提供的文件描述符上的锁,以确保遵守POSIX指定的行为,并且只要您生成要写入的数据而没有任何锁,该文件描述符锁便是唯一的锁。整个数据路径。 无论您在代码中执行什么操作,该锁都将位于您的数据路径中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM