螺纹安全和管道到流

Thread safety and piping to streams

本文关键字:管道 安全      更新时间:2023-10-16

如果两个不同的线程分别尝试将一段数据写入输出流,是否可以保证此操作对std::string_streamstd::coutstd::errstd::fstream(在C++11以后(是线程安全的?(换句话说,是否可以保证从多个线程写入同一流不会破坏流或交错单个数据片段?

我所说的"一条数据"是指"使用标准库重载<<调用管道操作员"。例如,如果一个线程连续写入包含一百万0的字符串,而另一个线程连续写入包含一百万1的字符串,是否可以保证不会有任何01混合?

测试这个想法的代码:这段代码在一个线程上输出一堆1,在另一个线程上输出一堆0。在将输出管道传输到文件时,我没有看到任何隔行扫描。 我在 Ubuntu 18.04 中测试了这段代码,使用gcc-7.3.0并使用标志-std=c++17 -pthread -O3 main.cc -o main进行编译,我没有观察到任何混合10s。

#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <chrono>
auto now() { 
return std::chrono::high_resolution_clock::now();
}
typedef decltype(now()) now_time_t;
int main(int argc, char** argv) {
std::stringstream A {}, B {};
int count = argc > 1 ? std::stoi(argv[1]) : 1000;
for(int i = 0; i < count; ++i) {
A << "0";
B << "1";
}
volatile bool waiting = true;
now_time_t t1_start_time, t2_start_time, t1_end_time, t2_end_time;
std::thread t1 = std::thread([&] { 
while(waiting) ;
t1_start_time = now();
std::cout << A.rdbuf(); 
t1_end_time = now();
});
std::thread t2 = std::thread([&] { 
while(waiting) ;
t2_start_time = now();
std::cout << B.rdbuf(); 
t2_end_time = now();
});
waiting = false;
t1.join();
t2.join();
auto t1_total_time = (t1_end_time - t1_start_time).count();
auto t2_total_time = (t2_end_time - t2_start_time).count();
std::cerr << "Time difference: " << (t1_start_time - t2_start_time).count() << std::endl;    
std::cerr << "t1 total time: " << t1_total_time << std::endl;    
std::cerr << "t2 total time: " << t2_total_time << std::endl;    
}

[iostreams.threadsafety]/1除非另有说明,否则多个线程对流对象 (30.8, 30.9(、流缓冲区对象 (30.6( 或 C 库流 (30.11( 的并发访问可能会导致数据争用 (4.7(。[注意:数据争用会导致未定义的行为 (4.7(。—尾注]

[iostream.objects.overview]/5多个线程并发访问同步 (30.5.3.4( 标准 iostream 对象的格式化和未格式化输入 (30.7.4.1( 和输出 (30.7.5.1( 函数或标准 C 流不会导致数据争用 (4.7(。[注意:如果用户希望避免交错字符,他们仍必须同步多个线程对这些对象和流的并发使用。—尾注]

这里的"标准iostream对象"是std::cinstd::coutstd::cerrstd::clog和相应的宽流之一(wcin等(。除非以前在其上调用过sync_with_stdio(false)否则此类流是同步的。

因此,对std::cout等人的并发写入不会导致数据争用,但允许交错字符。对任何其他流(例如字符串流、文件流(的并发写入通过数据争用表现出未定义的行为。