如果采用以下方法
template<typename... Args>
void report(std::ostream&str, Args&&... args)
{
(str << ... << args);
}
从具有相同str
的不同线程同步调用,来自不同线程的写入可能被破坏。因此,我改用
template<typename... Args>
void report(std::ostream&str, Args&&... args)
{
std::ostringstream ostr;
(ostr << ... << args);
str << ostr.str();
}
现在,我想知道是否有可能检测到这是否确实是必要的,即
{
if(is_global_object(str)) { // how to implement this?
std::ostringstream ostr;
(ostr << ... << args);
str << ostr.str();
} else
(str << ... << args);
}
问题:如何实施is_global_object(str)
?我很乐意发现所有的std::cout
,std::wcout
,std::clog
,std::wclog
,std::cerr
,std::wcerr
。
不,绝对不可能。如果一个线程创建一个本地ostream
然后将引用传递给另一个线程怎么办?不是全球性的,但仍然是共享的对象的全局性并不决定其位置。
首先,没有办法检查某个引用是否是对全局对象的引用。至少没有一些硬平台特定的黑客攻击(静态内存扫描?)可能无法正常工作。不要走那条路。
其次(更重要的是)你的问题不是“全球性的”。它是“共享的”。为什么您认为变量必须是全局的才能共享?另一方面,可能存在不共享的全局变量。但无论如何,这也没有解决方案。没有黑客会帮助你。
第三,您的中间代码也不是线程安全的。无法保证单个写入必须是线程安全的。在某些情况下,它是(std :: cout),但std :: ostream没有这样的保证。我可以使用非线程安全写入轻松编写自己的流。
最后但并非最不重要的是,您可以通过利用类和互斥体来避免所有这些。像这样的东西:
class Logger {
public:
Logger(std::ostream& stream): stream {stream}
{};
template<typename... Args>
void log(Args&&... args)
{
std::lock_guard<std::mutex> lg {mu};
(stream << ... << args);
}
private:
std::ostream& stream;
std::mutex mu;
};
并且根本不担心所有线程安全。
为了更好地扩展,您可能需要查看一些无锁的消息排队。