c++ - 用于 C++ 的基于行的线程安全 std::cerr

标签 c++ multithreading iostream stderr subclassing

创建我自己的 std::cerr 以使其逐行线程安全的最简单方法是什么。

我最好寻找代码来完成它。

我需要的是让一个线程生成的一行输出(以std::endl结束)保持作为一行输出 当我实际在我的控制台上看到它时(并且没有与其他线程的输出混合)。


解决方案:std::cerr 比 cstdio 慢很多。我更喜欢在 CriticalSectionLocker 类中使用 fprintf(stderr, "The message"),该类的构造函数获取线程安全锁,析构函数释放它。

最佳答案

如果可用,osyncstream (C++20) 解决了这个问题:

#include <syncstream> // C++20

std::osyncstream tout(std::cout);
std::osyncstream terr(std::cerr);

如果上述功能不可用,这里有一个插入式头文件,其中包含两个宏,用于线程安全地写入 std::coutstd::cerr (必须共享一个互斥量以避免输出交错)。这些基于两个 other answers ,但我做了一些更改,以便轻松放入现有代码库。这适用于 C++11 及更高版本。

我在一个 4 核处理器上用 4 个线程测试了这个,每个线程每秒写入 25,000 行到 tout 并偶尔输出到 terr,它解决了输出交错问题。与基于结构的解决方案不同,当放入此头文件时,我的应用程序没有可衡量的性能损失。我能想到的唯一缺点是,由于它依赖于宏,因此不能将它们放入命名空间。

线程流.h

#ifndef THREADSTREAM
#define THREADSTREAM

#include <iostream>
#include <sstream>
#include <mutex>

#define terr ThreadStream(std::cerr)
#define tout ThreadStream(std::cout)

/**
 * Thread-safe std::ostream class.
 *
 * Usage:
 *    tout << "Hello world!" << std::endl;
 *    terr << "Hello world!" << std::endl;
 */
class ThreadStream : public std::ostringstream
{
    public:
        ThreadStream(std::ostream& os) : os_(os)
        {
            // copyfmt causes odd problems with lost output
            // probably some specific flag
//            copyfmt(os);
            // copy whatever properties are relevant
            imbue(os.getloc());
            precision(os.precision());
            width(os.width());
            setf(std::ios::fixed, std::ios::floatfield);
        }

        ~ThreadStream()
        {
            std::lock_guard<std::mutex> guard(_mutex_threadstream);
            os_ << this->str();
        }

    private:
        static std::mutex _mutex_threadstream;
        std::ostream& os_;
};

std::mutex ThreadStream::_mutex_threadstream{};

#endif

test.cc

#include <thread>
#include <vector>
#include <iomanip>
#include "threadstream.h"

void test(const unsigned int threadNumber)
{
    tout << "Thread " << threadNumber << ": launched" << std::endl;
}

int main()
{
    std::locale mylocale(""); // get global locale
    std::cerr.imbue(mylocale); // imbue global locale
    std::ios_base::sync_with_stdio(false); // disable synch with stdio (enables input buffering)

    std::cout << std::fixed << std::setw(4) << std::setprecision(5);
    std::cerr << std::fixed << std::setw(2) << std::setprecision(2);

    std::vector<std::thread> threads;

    for (unsigned int threadNumber = 0; threadNumber < 16; threadNumber++)
    {
        std::thread t(test, threadNumber);
        threads.push_back(std::move(t));
    }

    for (std::thread& t : threads)
    {
        if (t.joinable())
        {
            t.join();
        }
    }

    terr << std::endl << "Main: " << "Test completed." << std::endl;

    return 0;
}

编译

g++ -g -O2 -Wall -c -o test.o test.cc
g++ -o test test.o -pthread

输出

./test
Thread 0: launched
Thread 4: launched
Thread 3: launched
Thread 1: launched
Thread 2: launched
Thread 6: launched
Thread 5: launched
Thread 7: launched
Thread 8: launched
Thread 9: launched
Thread 10: launched
Thread 11: launched
Thread 12: launched
Thread 13: launched
Thread 14: launched
Thread 15: launched

Main: Test completed.

关于c++ - 用于 C++ 的基于行的线程安全 std::cerr,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4446484/

相关文章:

python-3.x - GEKKO中优化问题的并行化

c++ - 如何将 C++ 输入流定界符包含到结果标记中

c++ - C++:在数组中输入数字时,第一个数字为0

c++ - 下面最后一句话(粗体)与复制抛出的异常有什么关系?

c++ - 编译时数据段太大

java - 如何找到线程何时完成?

java - AtomicReference getAndSet 和其他此类方法中字段 valueOffSet 的作用是什么

c++ - 将字符串附加到日志文件

c++ - 是否有任何 C++ 工具可以检查常见的未指定行为?

c++ - 旋转对象以指向鼠标位置