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

时间:2016-05-13 15:37:00

标签: 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("")
    }
}

完美无缺。但是,我刚刚添加了线程,我不确定这段代码是否是线程安全的。有什么提示吗?

2 个答案:

答案 0 :(得分:1)

您可以通过声明地图 extern来使此线程安全。如果您要在翻译单元中使用它,请将其设为static并在一个 翻译单元中定义,否则{{ 1}}很好。

您仍需要同步将日志写入磁盘。 互斥锁应修复:

// 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("");
    }
}

答案 1 :(得分:1)

在POSIX系统上,如果您始终将数据写入文件末尾,则多线程将数据写入文件的最快方法是使用低级C风格的open()追加模式,只需拨打write(),因为POSIX standard for write()状态:

  

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

     

...

     

如果设置了文件状态标志的O_APPEND标志,则为文件偏移量   应在每次写入之前设置为文件的末尾,否则   干预文件修改操作应在更改之间进行   文件偏移和写操作。

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

不需要互斥锁。

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

  

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

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