我正在使用一些游戏项目的多线程代码,并且厌倦了通过两个线程使用cout创建的stdout呕吐物来排序,以便在同一时间调试消息。我做了一些研究,并在提出“某些事情”之前盯着墙壁一两个小时。以下代码使用SFML进行计时和线程处理。 SFML互斥锁只是将窗口中的关键部分封装起来。线程安全cout技术。我错过了什么吗?
页眉:
#include <SFML\System.hpp>
#include <iostream>
class OutputStreamHack
{
public:
OutputStreamHack();
~OutputStreamHack();
ostream& outputHijack(ostream &os);
private:
sf::Clock myRunTime;
sf::Mutex myMutex;
};
static OutputStream OUTHACK;
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue);
实现:
#include <SFML\System.hpp>
#include <iostream>
#include "OutputStreamHack.h"
using namespace std;
OutputStreamHack::OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
OutputStreamHack::~OutputStreamHack()
{
myMutex.Unlock();
myRunTime.Reset();
}
ostream& OutputStreamHack::outputHijack(ostream &os)
{
sf::Lock lock(myMutex);
os<<"<"<<myRunTime.GetElapsedTime()<<","<<GetCurrentThreadId()<<"> "<<flush;
return os;
}
ostream& operator<<(ostream& os, const OutputStreamHack& inputValue)
{
OUTHACK.outputHijack(os);
return os;
}
用法:
cout<<OUTHACK<<val1<<val2<<val3....<<endl;
好了,所以这种工作方式是通过通过锁定强加线程安全的重载的插入操作符一个静态对象中的迭代器,然后刷新缓冲区。如果我正确理解了这个过程(我主要是一位自学的程序员),cout会从尾部到头部处理插入链的元素,并将每个元素的链上的ostream变量传递给流中的每个元素。一旦到达OUTHACK元素,就会调用重载的操作符,互斥锁被锁定,流被刷新。
为了验证目的,我在输出中添加了一些time/thread id调试信息。到目前为止,我的测试表明这种方法是有效的。我有几个线程用多个参数敲击cout,并且所有内容都以正确的顺序出现。
从我在研究这个问题时读到的内容来看,在cout中缺少线程安全似乎是人们在冒险进入线程化编程时遇到的一个相当常见的问题。我试图弄清楚的是,如果我使用的技术是解决这个问题的简单方法,或者我认为我很聪明,但错过了一些重要的东西。
根据我的经验,当用来描述编程的时候,聪明这个词只是延迟疼痛的代码字。我在这里做些什么,或者只是在周围追逐糟糕的黑客?
谢谢!
它运作的事实是纯粹的运气。只有时间和线程ID的输出受互斥量保护。在'OUTHACK'和'val1'之间潜入另一个线程是完全可能的。 – 2012-03-02 02:25:11
首先考虑写入'ostringstream',然后将内容在一个操作中转储到'cout'。 ''cout'通常是线程安全的,只是每次调用的锁定都很明显,每个'<<'操作都是一个独特的调用....这样,应用程序代码中就不会再有额外的锁定 - 这只能减少并行性。 – 2012-03-02 02:28:01
相关:[是cout同步/线程安全吗?](http://stackoverflow.com/questions/6374264/is-cout-synchronized-thread-safe/6374525#6374525) – legends2k 2014-06-06 11:49:10