螺纹安全和管道流

时间:2018-05-14 03:38:08

标签: c++ multithreading c++11 concurrency thread-safety

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

通过&#34;一个数据&#34;,我的意思是&#34;一次调用管道操作员<<使用标准库重载&#34;。例如,如果一个线程连续写入一个包含一百万0个字符串的字符串,而另一个线程连续写入一个包含一百万1个字符串的字符串,那么是否有任何保证赢得&# 39; t是与0 s混合的任何1

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

#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;    
}

1 个答案:

答案 0 :(得分:2)

  

[iostreams.threadsafety] / 1 多个线程对流对象(30.8,30.9),流缓冲区对象(30.6)或C库流(30.11)的并发访问可能会导致数据竞赛(4.7),除非另有规定(30.4)。 [注意:数据争用导致未定义的行为(4.7)。 -end note ]

     

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

此处“标准iostream对象”是std::cinstd::coutstd::cerrstd::clog和相应的宽流(wcin等)之一。除非先前已在其上调用sync_with_stdio(false),否则此类流将同步。

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