如何做到流式输出的thread safe

chenyu2202863 2010-09-21 09:35:58
比如在这样的一个log日志组件中,提供对每个operator<<调用的线程安全,形如

template<typename StreamT, typename T>
inline StreamT &operator<<(StreamT &log, const T &val)
{
log.Print(val);

return log;
}

template<typename T>
void Print(const T &val)
{
Mutex mutex;

Out(...);
}



虽然每次的输入会是安全的,但遇到如下情况则不然
log << 1 << "2" << 3.0;

这个会有三次operator<<调用,而函数调用之间没有安全保证~

请问,如何做到形如上述情况的线程安全~(当然是多线程共同操作log)
...全文
348 34 打赏 收藏 转发到动态 举报
写回复
用AI写文章
34 条回复
切换为时间正序
请发表友善的回复…
发表回复
chenyu2202863 2010-09-27
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 yutaooo 的回复:]
在几次流输出间,保持线程安全,比较正规做法是使用flockfile() funlockfile()这样的函数进行保护。

稍微大一些的项目,在使用多线程的情况下,log很容易就会成为整个应用的瓶颈,并且很难保证即是同步又是高性能。一个已经被应用的方案是采用每线程一个输出文件,然后再在外部由专门的程序进行日志合并。实现上也许用些线程局部存储之类的技术。
[/Quote]

谢谢,很有参考价值
cunsh 2010-09-26
  • 打赏
  • 举报
回复
其实这里有个问题,在unlock和lock之间的这段时间,得不到强力保证~,有可能出现竞争导致unsafe
=============
gStream << 1 << "2" << 3.0;
这里应该只会lock一次. unlock一次吧.
chenyu2202863 2010-09-26
  • 打赏
  • 举报
回复
[Quote=引用 27 楼 cunsh 的回复:]
这样就没法连写了,因为StreamT2是右值啊!怎么解决?
=====

struct StreamT
{
template<class T>
void print(cosnt T& t) {}

void lock();
void unlock();
};


StreamT gStream;


// 让下面这个代理前面的 StreamT ……
[/Quote]

其实这里有个问题,在unlock和lock之间的这段时间,得不到强力保证~,有可能出现竞争导致unsafe
yutaooo 2010-09-26
  • 打赏
  • 举报
回复

在几次流输出间,保持线程安全,比较正规做法是使用flockfile() funlockfile()这样的函数进行保护。

稍微大一些的项目,在使用多线程的情况下,log很容易就会成为整个应用的瓶颈,并且很难保证即是同步又是高性能。一个已经被应用的方案是采用每线程一个输出文件,然后再在外部由专门的程序进行日志合并。实现上也许用些线程局部存储之类的技术。
chenyu2202863 2010-09-26
  • 打赏
  • 举报
回复
倒是有一个解决方案,或许仁者见仁智者见智,不一定每个人都喜欢

template<typename LogT>
class ThreadSafeAgent
{
typedef DataStructure::AutoSpinLock LockType;

LockType lock_;
LogT &log_;

public:
ThreadSafeAgent(LogT &log)
: log_(log)
{
lock_.Lock();
}
~ThreadSafeAgent()
{
lock_.Unlock();
}

public:
LogT &operator()()
{
return log_;
}
};

template<typename LogT>
inline ThreadSafeAgent<LogT> ThreadSafe(LogT &log)
{
return ThreadSafeAgent<LogT>(log);
}

template<typename LogT, typename T>
inline ThreadSafeAgent<LogT> &operator<<(ThreadSafeAgent<LogT> &log, const T &val)
{
log() << val;
return log;
}


ThreadSafe(multiLog)() << 12 << "34" << 5.6 << Endl;

在上面的代码中,ThreadSafe(multiLog)()实际返回multilog,但是经过了一次加锁的过程,离开作用域后就会调用析构进行解锁。
chenyu2202863 2010-09-26
  • 打赏
  • 举报
回复
当然,在你最后的示例里传回的引用是一次构造析构。
试试,思路有了
chenyu2202863 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 yutaooo 的回复:]
c++的线程安全问题,没有纳入到标准中去考量。在实际的库实现中,也许某些实现的确实现了线程安全。因此,在讨论c++的线程安全时,要说一下是用的那个库。

c的,已经知道的,glibc的stdio是线程安全的。
[/Quote]

老兄,你没看清楚问题吧··
我是说形如log << 1 << "2" << 3.0;这样的调用(3个operator<<函数调用)如何做到原子性操作,但是不能让用户自行加锁等机制来保证串行化
cunsh 2010-09-25
  • 打赏
  • 举报
回复

使用还是原来的样子
gStream << 1 << "2" << 3.0;
cunsh 2010-09-25
  • 打赏
  • 举报
回复
这样就没法连写了,因为StreamT2是右值啊!怎么解决?
=====

struct StreamT
{
template<class T>
void print(cosnt T& t) {}

void lock();
void unlock();
};


StreamT gStream;


// 让下面这个代理前面的 StreamT 自动锁和解锁
struct StreamT2
{
StreamT& mStreamT;
StreamT2(StreamT& s) : mStreamT(s) { mStreamT.lock(); }
~StreamT2() { mStreamT.unlock(); }
}

template<typename StreamT2, typename T>
inline const StreamT2 &operator<<(const StreamT2 &log, const T &val)
{
log.mStream.Print(val);
return log;
}


我的思路大体这样. 不过加个 const 貌似很难看. 不知有没其它问题. 给楼主参考..
赵4老师 2010-09-25
  • 打赏
  • 举报
回复
摒弃cout
使用printf
gules 2010-09-25
  • 打赏
  • 举报
回复
LZ:建议你最好找个支持thread safe的IO流库,直接研究其源码,看看找不找得到答案。
反正我一般在多线程程序不直接用<<,而用函数代替。
iambic 2010-09-25
  • 打赏
  • 举报
回复
找个现成的logging库吧。或者照着自己山寨一个。
chenyu2202863 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 cunsh 的回复:]
我有个思路楼主看看有没问题:

每个流都有个锁. 然后 operator<<() 函数返回一个其它类型的对象. 如:

inline StreamT2 operator<<(StreamT &log, const T &val)

让 StreamT2 继续锁住 StreamT 那个锁. 并且继续干 StreamT 的事.

大体上就是这么个意思. 它是个临时……
[/Quote]
或许提供外部锁是唯一方便且可行的方式了·
chenyu2202863 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 qinqijing_123 的回复:]
楼主需要的原子性操作在《windows核心编程》第7章有,可以看看,我就不多打字了...
[/Quote]

大叔,你的图片也真~~
你误解我意思了,请看别人的回答~谢谢
chenyu2202863 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 taodm 的回复:]
写成log(1 << "2" << 3.0);也不会死人的。
[/Quote]

这个~~手法,不会,情taodom示下
gules 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 19 楼 cunsh 的回复:]
我有个思路楼主看看有没问题:

每个流都有个锁. 然后 operator<<() 函数返回一个其它类型的对象. 如:

inline StreamT2 operator<<(StreamT &log, const T &val)

让 StreamT2 继续锁住 StreamT 那个锁. 并且继续干 StreamT 的事.

大体上就是这么个意思. 它是个临时……
[/Quote]
这样就没法连写了,因为StreamT2是右值啊!怎么解决?
cunsh 2010-09-25
  • 打赏
  • 举报
回复

我有个思路楼主看看有没问题:

每个流都有个锁. 然后 operator<<() 函数返回一个其它类型的对象. 如:

inline StreamT2 operator<<(StreamT &log, const T &val)

让 StreamT2 继续锁住 StreamT 那个锁. 并且继续干 StreamT 的事.

大体上就是这么个意思. 它是个临时对象. 构造是锁住. 销毁时解锁.
airtrack 2010-09-25
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 pengzhixi 的回复:]

引用 14 楼 taodm 的回复:
写成log(1 << "2" << 3.0);也不会死人的。

taodm这个是什么函数呢?
[/Quote]
宏?

#define LOG(expr) \
do \
{ \
log.lock(); \
log << expr; \
log.unlock(); \
} while (0)
luoqi 2010-09-25
  • 打赏
  • 举报
回复
class _XX{
syncobj m_atom;//类似于Mutex
public:
void operator<<(const char* s){
m_atom.lock();
printf(s);
m_atom.unlock();
}
};
Rainqin123 2010-09-25
  • 打赏
  • 举报
回复
楼主需要的原子性操作在《windows核心编程》第7章有,可以看看,我就不多打字了...
加载更多回复(13)

64,676

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧