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

发布于 2024-10-08 00:33:43 字数 388 浏览 6 评论 0原文

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

我最好寻找代码来做到这一点。

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


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

What is the easiest way to create my own std::cerr so that it is line-by-line thread-safe.

I am preferably looking for the code to do it.

What I need is so that a line of output (terminated with std::endl) generated by one thread stays as a line of output when I actually see it on my console (and is not mixed with some other thread's output).


Solution: std::cerr is much slower than cstdio. I prefer using fprintf(stderr, "The message") inside of a CriticalSectionLocker class whose constructor acquires a thread-safe lock and the destructor releases it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

你穿错了嫁妆 2024-10-15 00:33:43

如果可用, osyncstream (C++20) 可以解决此问题:

#include <syncstream> // C++20

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

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

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

threadstream.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.

If available, osyncstream (C++20) solves this problem:

#include <syncstream> // C++20

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

If the above feature is not available, here is a drop-in header file containing two macros for thread-safe writing to std::cout and std::cerr (which must share a mutex in order to avoid interleaving of output). These are based on two other answers, but I have made some changes to make it easy to drop into an existing code base. This works with C++11 and forward.

I've tested this with 4 threads on a 4-core processor, with each thread writing 25,000 lines per second to tout and occasional output to terr, and it solves the output interleaving problem. Unlike a struct-based solution, there was no measurable performance hit for my application when dropping in this header file. The only drawback I can think of is that since this relies on macros, they can't be placed into a namespace.

threadstream.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;
}

compiling

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

output

./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.
临风闻羌笛 2024-10-15 00:33:43

这是我在某个时候编写的基于线程安全行的日志记录解决方案。它使用 boost mutex 来保证线程安全。它比必要的稍微复杂一些,因为您可以插入输出策略(它应该转到文件、stderr 还是其他地方?):

logger.h:

#ifndef LOGGER_20080723_H_
#define LOGGER_20080723_H_

#include <boost/thread/mutex.hpp>
#include <iostream>
#include <cassert>
#include <sstream>
#include <ctime>
#include <ostream>

namespace logger {
    namespace detail {

        template<class Ch, class Tr, class A>
        class no_output {
        private:
            struct null_buffer {
                template<class T>
                null_buffer &operator<<(const T &) {
                    return *this;
                }
            };
        public:
            typedef null_buffer stream_buffer;

        public:
            void operator()(const stream_buffer &) {
            }
        };

        template<class Ch, class Tr, class A>
        class output_to_clog {
        public:
            typedef std::basic_ostringstream<Ch, Tr, A> stream_buffer;
        public:
            void operator()(const stream_buffer &s) {
                static boost::mutex mutex;
                boost::mutex::scoped_lock lock(mutex);
                std::clog << now() << ": " << s.str() << std::endl;
            }

        private:
            static std::string now() {
                char buf[64];
                const time_t tm = time(0);  
                strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tm));
                return buf;
            }

        };

        template<template <class Ch, class Tr, class A> class OutputPolicy, class Ch = char, class Tr = std::char_traits<Ch>, class A = std::allocator<Ch> >
        class logger {
            typedef OutputPolicy<Ch, Tr, A> output_policy;
        public:
            ~logger() {
                output_policy()(m_SS);
            }
        public:
            template<class T>
            logger &operator<<(const T &x) {
                m_SS << x;
                return *this;
            }
        private:
            typename output_policy::stream_buffer m_SS;
        };
    }

    class log : public detail::logger<detail::output_to_clog> {
    };
}

#endif

用法如下所示:

logger::log() << "this is a test" << 1234 << "testing";

注意缺少 '\n 'std::endl 因为它是隐式的。内容被缓冲,然后使用模板指定的策略自动输出。此实现还在该行前面添加了时间戳,因为它用于记录目的。 no_output 策略是严格可选的,当我想禁用日志记录时我会使用它。

Here's a thread safe line based logging solution I cooked up at some point. It uses boost mutex for thread safety. It is slightly more complicated than necessary because you can plug in output policies (should it go to a file, stderr, or somewhere else?):

logger.h:

#ifndef LOGGER_20080723_H_
#define LOGGER_20080723_H_

#include <boost/thread/mutex.hpp>
#include <iostream>
#include <cassert>
#include <sstream>
#include <ctime>
#include <ostream>

namespace logger {
    namespace detail {

        template<class Ch, class Tr, class A>
        class no_output {
        private:
            struct null_buffer {
                template<class T>
                null_buffer &operator<<(const T &) {
                    return *this;
                }
            };
        public:
            typedef null_buffer stream_buffer;

        public:
            void operator()(const stream_buffer &) {
            }
        };

        template<class Ch, class Tr, class A>
        class output_to_clog {
        public:
            typedef std::basic_ostringstream<Ch, Tr, A> stream_buffer;
        public:
            void operator()(const stream_buffer &s) {
                static boost::mutex mutex;
                boost::mutex::scoped_lock lock(mutex);
                std::clog << now() << ": " << s.str() << std::endl;
            }

        private:
            static std::string now() {
                char buf[64];
                const time_t tm = time(0);  
                strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&tm));
                return buf;
            }

        };

        template<template <class Ch, class Tr, class A> class OutputPolicy, class Ch = char, class Tr = std::char_traits<Ch>, class A = std::allocator<Ch> >
        class logger {
            typedef OutputPolicy<Ch, Tr, A> output_policy;
        public:
            ~logger() {
                output_policy()(m_SS);
            }
        public:
            template<class T>
            logger &operator<<(const T &x) {
                m_SS << x;
                return *this;
            }
        private:
            typename output_policy::stream_buffer m_SS;
        };
    }

    class log : public detail::logger<detail::output_to_clog> {
    };
}

#endif

Usage looks like this:

logger::log() << "this is a test" << 1234 << "testing";

note the lack of a '\n' and std::endl since it's implicit. The contents are buffered and then atomically output using the template specified policy. This implementation also prepends the line with a timestamp since it is for logging purposes. The no_output policy is stricly optional, it's what I use when I want to disable logging.

無處可尋 2024-10-15 00:33:43

这:

#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;}

适用于大多数编译器,适用于常见情况 myerr("ERR: " << message << number)

This:

#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;}

works on most compilers for the common case of myerr("ERR: " << message << number).

征﹌骨岁月お 2024-10-15 00:33:43

为什么不直接创建一个锁定类并在您想要执行线程安全 IO 的任何地方使用它呢?

class LockIO
{
    static pthread_mutex_t *mutex;  
public:
    LockIO() { pthread_mutex_lock( mutex ); }
    ~LockIO() { pthread_mutex_unlock( mutex ); }
};

static pthread_mutex_t* getMutex()
{
    pthread_mutex_t *mutex = new pthread_mutex_t;
    pthread_mutex_init( mutex, NULL );
    return mutex;
}
pthread_mutex_t* LockIO::mutex = getMutex();

然后你将任何你想要的 IO 放入一个块中:

std::cout <<"X is " <<x <<std::endl;

变成:

{
    LockIO lock;
    std::cout <<"X is " <<x <<std::endl;
}

Why not just create a locking class and use it where ever you want to do thread-safe IO?

class LockIO
{
    static pthread_mutex_t *mutex;  
public:
    LockIO() { pthread_mutex_lock( mutex ); }
    ~LockIO() { pthread_mutex_unlock( mutex ); }
};

static pthread_mutex_t* getMutex()
{
    pthread_mutex_t *mutex = new pthread_mutex_t;
    pthread_mutex_init( mutex, NULL );
    return mutex;
}
pthread_mutex_t* LockIO::mutex = getMutex();

Then you put any IO you want in a block:

std::cout <<"X is " <<x <<std::endl;

becomes:

{
    LockIO lock;
    std::cout <<"X is " <<x <<std::endl;
}
淡紫姑娘! 2024-10-15 00:33:43

对 unixman 评论中的方法的改进(这并不真正适合评论)。

#define LOCKED_ERR \
    if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \
    else std::cerr

就可以使用它

LOCKED_ERR << "ERR: " << message << endl;

如果仔细实现 ErrCriticalSectionLocker,

。但是,我个人更喜欢肯的建议。

An improvement (that doesn't really fit in a comment) on the approach in unixman's comment.

#define LOCKED_ERR \
    if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \
    else std::cerr

Which can be used like

LOCKED_ERR << "ERR: " << message << endl;

if ErrCriticalSectionLocker is implemented carefully.

But, I would personally prefer Ken's suggestion.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文