创建自己的std::cerr
的最简单方法是什么,以便它是逐行线程安全的。
我最好找代码来做。
我需要的是当一个线程生成的a line of output
(以std::endl
终止)在我的控制台上实际看到它时保持as a line of output
(并且没有与其他线程混合使用)输出)。
解决方案:std::cerr
比cstdio慢。我更喜欢在fprintf(stderr, "The message")
类中使用CriticalSectionLocker
,其构造函数获取线程安全锁并且析构函数释放它。
答案 0 :(得分:3)
这是我在某个时刻制作的基于线程安全线的日志记录解决方案。它使用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
策略是严格可选的,它是我在禁用日志记录时使用的。
答案 1 :(得分:3)
如果可用,osyncstream(C ++ 20)解决了此问题:
#include <syncstream> // C++20
std::osyncstream tout(std::cout);
std::osyncstream terr(std::cerr);
如果上述功能不可用,则这是一个插入头文件,其中包含两个宏,用于线程安全地写入std::cout
和std::cerr
(它们必须共享一个互斥体,以避免交织)输出)。这些基于两个other answers,但是我进行了一些更改以使其易于放入现有代码库中。这适用于C ++ 11及更高版本。
我已经在4核处理器上用4个线程对此进行了测试,每个线程每秒将25,000行写入tout
,偶尔将输出写入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.
答案 2 :(得分:2)
此:
#define myerr(e) {CiriticalSectionLocker crit; std::cerr << e << std::endl;}
对于myerr("ERR: " << message << number)
的常见情况,适用于大多数编译器。
答案 3 :(得分:2)
为什么不创建一个锁定类并在想要进行线程安全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;
}
答案 4 :(得分:1)
对unixman评论中的方法进行了改进(实际上并不适用于评论)。
#define LOCKED_ERR \
if(ErrCriticalSectionLocker crit = ErrCriticalSectionLocker()); \
else std::cerr
可以像
一样使用LOCKED_ERR << "ERR: " << message << endl;
如果仔细实施ErrCriticalSectionLocker。
但是,我个人更喜欢肯的建议。