如果采用以下方法
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
。
答案 0 :(得分:3)
不,绝对不可能。如果一个线程创建一个本地ostream
然后将对它的引用传递给另一个线程怎么办?不是全球性的,但仍然共享。对象的全局性并不能确定其位置。
答案 1 :(得分:1)
因此,首先无法检查某些引用是否是对全局对象的引用。至少并非没有一些特定于平台的硬黑客(静态内存扫描?),这些黑客可能无法正常工作。不要走那条路。
第二个(更重要的是)您的问题不是“全局”的。它是“共享的”。您为什么认为变量必须是全局变量才能共享?另一方面,可能存在不共享的全局变量。但是无论如何,也没有解决方案。没有hack可以帮助您。
第三,您的中间代码也不是线程安全的。不能保证单次写入必须是线程安全的。在某些情况下,它是(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;
};
完全不用担心所有线程安全问题。
为了更好地扩展,您可能需要看一下消息的无锁排队。