一般来说,我假设流不同步,由用户做适当的锁定.但是,做cout标准库中的特殊处理吗?
也就是说,如果多个线程正在写入,cout它们会破坏cout对象吗?据我所知,即使同步,你仍然会得到随机交错的输出,但保证交错.也就是说,cout从多个线程使用是否安全?
该供应商是否依赖?gcc做什么?
重要提示:如果您说"是",请为您的答案提供某种参考,因为我需要某种证明.
我关注的还不是基础系统调用,这些都很好,但是流在顶部添加了一层缓冲.
R. *_*des 102
C++ 03标准没有说明任何内容.当你不能保证某些东西的线程安全时,你应该把它视为不是线程安全的.
这里特别感兴趣的cout是缓冲的事实.即使对write(或在该特定实现中实现该效果的任何内容)的调用保证是互斥的,缓冲区也可以由不同的线程共享.这将很快导致流内部状态的破坏.
即使保证对缓冲区的访问是线程安全的,您认为在此代码中会发生什么?
// in one thread
cout << "The operation took " << result << " seconds.";
// in another thread
cout << "Hello world! Hello " << name << "!";
Run Code Online (Sandbox Code Playgroud)
您可能希望此处的每一行都互相排斥.但是如何实现保证呢?
在C++ 11中,我们确实有一些保证.FDIS在§27.4.1[iostream.objects.overview]中说明以下内容:
并发访问同步(§27.5.3.4)标准iostream对象的格式化和未格式化输入(§27.7.2.1)和输出(§27.7.3.1)函数或多个线程的标准C流不应导致数据竞争(§ 1.10).[注意:如果用户希望避免交错字符,则仍必须通过多个线程同步这些对象和流的并发使用. - 结束说明]
因此,您不会受到损坏的流,但如果您不希望输出是垃圾,则仍需要手动同步它们.
Nem*_*emo 15
这是一个很好的问题.
首先,C++ 98/C++ 03没有"线程"的概念.所以在那个世界里,这个问题毫无意义.
那么C++ 0x呢?看马蒂纽的答案(我承认让我感到惊讶).
C++ 0x之前的特定实现怎么样?好吧,例如,这是basic_streambuf<...>:sputc来自GCC 4.5.2("streambuf"标题)的源代码:
int_type
sputc(char_type __c)
{
int_type __ret;
if (__builtin_expect(this->pptr() < this->epptr(), true)) {
*this->pptr() = __c;
this->pbump(1);
__ret = traits_type::to_int_type(__c);
}
else
__ret = this->overflow(traits_type::to_int_type(__c));
return __ret;
}
Run Code Online (Sandbox Code Playgroud)
显然,这不执行锁定.也没有xsputn.这绝对是cout使用的streambuf的类型.
据我所知,libstdc ++不会对任何流操作执行任何锁定.我不指望任何,因为那会很慢.
所以这个实现,显然是可能的两个线程输出腐败对方(不只是交错).
这段代码可能会破坏数据结构本身吗?答案取决于这些功能的可能相互作用; 例如,如果一个线程试图刷新缓冲区而另一个线程试图调用xsputn或其他什么,会发生什么.它可能取决于您的编译器和CPU如何决定重新排序内存加载和存储; 需要仔细分析才能确定.如果两个线程试图同时修改同一个位置,它还取决于你的CPU做什么.
换句话说,即使它在当前环境中正常工作,在更新任何运行时,编译器或CPU时也可能会中断.
执行摘要:"我不会".构建一个正确锁定的日志记录类,或者移动到C++ 0x.
作为一个弱选择,您可以将cout设置为无缓冲.很可能(虽然不能保证)会跳过与缓冲区相关的所有逻辑并write直接调用.虽然这可能会非常缓慢.
C++标准没有规定写入流是否是线程安全的,但通常不是.
www.techrepublic.com/article/use-stl-streams-for-easy-c-plus-plus-thread-safe-logging
还有:C++中的标准输出流是否是线程安全的(cout,cerr,clog)?
UPDATE
请查看@Martinho Fernandes的回答,了解新标准C++ 11对此的了解.
正如其他答案所提到的,这绝对是特定于供应商的,因为C++标准没有提到线程(这在C++ 0x中有所改变).
GCC没有对线程安全和I/O做出很多承诺.但它所做的承诺的文件在这里:
关键的东西可能是:
__basic_file类型只是C stdio层周围的小包装器的集合(再次,请参阅Structure下的链接).我们没有锁定自己,只是简单地通过调用fopen,fwrite等等.
因此,对于3.0,"多线程安全I/O"的问题必须回答,"您的平台的C库线程是否适用于I/O?" 有些是默认的,有些则不是; 许多提供C库的多个实现,具有不同的线程安全性和效率.作为程序员,您始终需要注意多个线程.
(作为一个例子,POSIX标准要求C STDIO FILE*操作都是原子.符合POSIX标准C库(例如,在Solaris和GNU/Linux)有一个内部互斥锁序列化对FILE*S的操作.但是,你仍然需要不做愚蠢的事情,比如在一个线程中调用fclose(fs),然后在另一个线程中调用fs.)
因此,如果您的平台的C库是线程安全的,那么您的fstream I/O操作将是最低级别的线程安全.对于更高级别的操作,例如操作流格式化类中包含的数据(例如,在std :: ofstream中设置回调),您需要像任何其他关键共享资源一样保护此类访问.
我不知道在提到的3.0时间范围内是否有任何改变.
MSVC的线程安全文档iostreams可以在这里找到:http://msdn.microsoft.com/en-us/library/c9ceah3b.aspx:
单个对象对于从多个线程读取是线程安全的.例如,给定对象A,可以安全地从线程1和线程2同时读取A.
如果一个线程正在写入单个对象,则必须保护对相同或其他线程上该对象的所有读写操作.例如,给定对象A,如果线程1写入A,则必须阻止线程2读取或写入A.
即使另一个线程正在读取或写入同一类型的不同实例,也可以安全地读取和写入一个类型的实例.例如,给定相同类型的对象A和B,如果在线程1中写入A并且在线程2中读取B,则是安全的.
...
iostream类
iostream类遵循与其他类相同的规则,但有一个例外.从多个线程写入对象是安全的.例如,线程1可以与线程2同时写入cout.但是,这可能导致两个线程的输出混合.
注意:从流缓冲区读取不被视为读取操作.它应该被视为写操作,因为这会改变类的状态.
请注意,该信息适用于最新版本的MSVC(目前适用于VS 2010/MSVC 10/cl.exe16.x).您可以使用页面上的下拉控件选择旧版MSVC的信息(旧版本的信息不同).
| 归档时间: |
|
| 查看次数: |
35867 次 |
| 最近记录: |