1.研究背景

  在测试时发现mingw版本的gcc编译出来的程序,一个主程序新建20个线程,每个线程都循环向cout输出信息,几分钟程序就崩了,而用msvc和gcc-linaro版gcc交叉编译器编译出来的运行很久都没问题。

2.相关查询

2.1 C++ iostreams: Unexpected but legal multithreaded behaviour

https://berthub.eu/articles/posts/iostreams-unexpected/

2.1.1Multi-threading

One reason for using C++ is that it supports multi-threading (or more broadly, multi-processing) very well. The original C++ standard had no words on it because back in the day, officially there were no threads. Later versions of C++ (starting with C++ 2011) dusted off the iostreams specification and added words on thread safety.

This starts off with the following:

Concurrent access to a stream object (30.8, 30.9), stream buffer object (30.6), or C Library stream (30.12) by multiple threads may result in a data race (6.8.2) unless otherwise specified (30.4). [ Note: Data races result in undefined behavior (6.8.2). — end note ] – [iostreams.threadsafety]

This is a blanket statement that bad things may happen if we do stuff to iostreams from several threads at the same time, unless there is a specific statement that says doing so is safe.

Luckily, there is the following paragraph too:

Concurrent access to a synchronized (27.5.3.4) standard iostream object’s formatted and unformatted input (27.7.2.1) and output (27.7.3.1) functions or a standard C stream by multiple threads shall not result in a data race (1.10). [Note: Users must still synchronize concurrent use of these objects and streams by multiple threads if they wish to avoid interleaved characters. — end note] – [iostream.objects.overview]

No disasters will happen on concurrent use of iostreams, although if you print out two log lines to cerr at the same time, you may find them interleaved in your output. This certainly is not pretty & hard to parse, but at least it is not illegal.

Note however that this paragraph talks only about ‘synchronized’ streams. Once we call the much recommended sync_with_stdio(false), our streams are no longer synchronized, not only not with stdio, but not at all. This means every write operation on cin or cout etc must now be protected by a mutex.

This itself is likely reason enough to never call sync_with_stdio(false) in any multi-threaded program using cout to print things.

2.1.2 Basic thread locking in C++11

Notice that the requirement not to produce a data race applies only to the standard iostream objects (cout, cin, cerr, clog, wcout, wcin, wcerr, and wclog) and only when they are synchronized (which they are by default and which can be disabled using the sync_with_stdio member function).

Unfortunately I've noticed two phenomena; implementations either provide stricter guarantees than required (e.g., thread synchronization for all stream objects no matter what, giving poor performance) or fewer (e.g., standard stream objects that are sync_with_stdio produce data races). MSVC seems to lean toward the former while libc++ leans toward the latter.

Anyway, as the note indicates, you have to provide mutual exclusion yourself if you want to avoid interleaved characters. Here's one way to do it:

std::mutex m;

struct lockostream {
std::lock_guard<std::mutex> l;
lockostream() : l(m) {}
}; std::ostream &operator<<(std::ostream &os, lockostream const &l) {
return os;
} std::cout << lockostream() << "Hello, World!\n";

This way a lock guard is created and lives for the duration of the expression using std::cout. You can templatized the lockostream object to work for any basic_*stream, and even on the address of the stream so that you have a seperate mutex for each one.

Of course the standard stream objects are global variables, so you might want to avoid them the same way all global variables should be avoided. They're handy for learning C++ and toy programs, but you might want to arrange something better for real programs.

2.1.3 or U could do like this

You have to use the normal locking techniques as you would do with any other resource otherwise you are experiencing UB.

std::mutex m;
std::lock_guard<std::mutex> lock(m);
std::cout << "hello hello"; 

or alternativly you can use printf which is threadsafe(on posix):

printf("hello hello");

2.1.4 Summarising

Be very careful when using std::ios_base::sync_with_stdio(false), and if you do, also issue cin.tie(nullptr). Make sure sync_with_stdio is called before doing any i/o.

In general, be very weary of doing output operations on a single iostream from multiple threads - it may not do what you want.

Some further reading:

  • The libstdc++ bug I filed about this, where it will likely be concluded this is (unfortunately) not a bug, but intended behaviour
  • The {fmt} library is a simpler alternative to rapidly output text. Typically faster than printf.

2.2 Simple Lock-free std::cout in C++ Multithreading

https://wasin.io/blog/17_simple-lock-free-std-cout-cpp-multithreading.html

Whenever you need to do a quick multithreading program in C++, most of the time printing something out via std::cout::operator<< to validate the logic is the most go-to solution.

Whenever at least two threads call std::cout::operator<< at the same time, then console result will probably be mess, not what we exepct. Newline might not get printed, space sometimes included but other time not included, etc.

Apply full std::mutex seems to be overkill. Anyway mutex solution is not lock-free. What’s about std::atomic? Real close, but it still doesn’t guarantee lock-free solution for us. So those two methods go out of the way.

The sane solution is to use std::atomic_flag. Lower level than std::atomic. It’s comparable to std::atomic<bool> but without load and store operation. See the following code

    static std::atomic_flag lock = ATOMIC_FLAG_INIT;

    // spin-lock (suitable if short time waiting is known beforehand)
while (lock.test_and_set(std::memory_order_acquire))
; std::cout << "Print something\n"; // release the lock
lock.clear();

Check ThreadLocal.cpp for full example of multiple threads trying to print something out at the same time.

Compile it with g++ -std=c++11 ThreadLocal.cpp -lpthread.

3.总结

由于默认情况下,sync_with_stdio是true的,标准定义多线程时的cout行为为UB(undefined behavior),所以不同编译器出现不同的现象也并不奇怪。

多线程环境下的cout建议还是使用atomic_flag或者std::lock_guard<std::mutex>方式,加锁实现时c++20的osyncstream 也是不错的方案。

多线程std::cout 深入研究的更多相关文章

  1. std::cout和printf

    禁止std::cout和printf混用,在多线程环境下可能导致coredump. 说明:printf和std::cout分别为标准c语言与c++中的函数,两者的缓冲区机制不同(printf无缓冲区, ...

  2. c/c++ 多线程 std::call_once的应用

    多线程 std::call_once的应用 std::call_once的应用:类成员的延迟初始化,并只初始化一次.和static的作用很像,都要求是线程安全的,c++11之前在多线程的环境下,sta ...

  3. c/c++ 多线程 std::call_once

    多线程 std::call_once 转自:https://blog.csdn.net/hengyunabc/article/details/33031465 std::call_once的特点:即使 ...

  4. C++11并发——多线程std::thread (一)

    https://www.cnblogs.com/haippy/p/3284540.html 与 C++11 多线程相关的头文件 C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是< ...

  5. C++ 多线程 std::thread 使用总结

    在C++ 11之前,官方并没有支持线程库.C++ 11通过标准库引入了对 thread 类的支持,大大方便了完成多线程开发的工作. std::thread 构造函数  (1)thread() noex ...

  6. Visual Studio将std::cout输出到Output窗口

    在debug的时候,输出到Output需要使用OutputDebugString函数,但部分库的log是采用std::cout输出的,需要用控制台(黑窗)程序来查看输出.有没有一种使用GUI和Outp ...

  7. std::cout彩色输出

    Mac OS效果 Windows 效果 想写这个东西其实是因为最近要写个命令行的工具,但是有个问题是什么呢?就是传统的那个黑漆漆的窗口看起来很蛋疼.并且完全看不到重点,于是就想起 来这么一个东西.相对 ...

  8. 02 - 用wxStreamToTextRedirector和wxTextCtrl输出std::cout

    遇到问题,单行显示, new line丢失 原因: wxTextCtrl默认是单行的 解决办法:使用wxTE_MULTILINE参数初始化wxTextCtrl wxTextCtrl *text = , ...

  9. c/c++ 多线程 std::lock

    多线程 std::lock 当要同时操作2个对象时,就需要同时锁定这2个对象,而不是先锁定一个,然后再锁定另一个.同时锁定多个对象的方法:std::lock(对象1.锁,对象2.锁...) 额外说明: ...

随机推荐

  1. Explain关键字解析

    Explain 用法 explain模拟Mysql优化器是如何执行SQL查询语句的,从而知道Mysql是如何处理你的SQL语句的.分析你的查询语句或是表结构的性能瓶颈. 语法:Explain + SQ ...

  2. 使用property取代getter和setter方法

    class Days(object): def __init__(self): self.__days = 0 @property def days(self):#获取函数,名字是days days ...

  3. The JOIN operation -- SQLZOO

    The JOIN operation 注意:where语句中对表示条件的需要用单引号, 下面的译文使用的是有道翻译如有不正确,请直接投诉有道 01.Modify it to show the matc ...

  4. Developer 转型记:一个开发平台的“魔力”

    摘要:开发者该如何借助AI技术,探索可沉淀的落地应用?在这AI技术浪潮下,实现完美的应用创新?我们一起来听听他的故事…… 随着政策的加持.技术快速的迭代,人工智能热潮正在蔓延.2020年,AI落地大考 ...

  5. 自制廉价的LED+LCD型投影仪

    文档标识符:PROJECTOR_T-D-P6 作者:DLHC 最后修改日期:2020.7.30 本文链接:https://www.cnblogs.com/DLHC-TECH/p/PROJECTOR_T ...

  6. ASP.NET Core 监听SQL Server数据库的实时信息

    1.开发环境: 开发工具:Visual Studio 2019 数据库:SQL Server2012 开发环境:.Net Core 3.1 2.使用技术: Signalr:实现消息推送 SqlDepe ...

  7. PHP chop() 函数

    实例 移除字符串右侧的字符: <?php$str = "Hello World!";高佣联盟 www.cgewang.comecho $str . "<br& ...

  8. 集合框架-HashMap&HashSet&LinkedHshMap

    一.HashMap的底层实现 HashMap底层是基于数组和链表实现的.其中最重要的参数:容量和负载因子. 容量的默认大小事16,负载因子是0.75,当HashMap的size>16*0.75的 ...

  9. electron开发 - mac关闭和隐藏窗口

    针对mac平台的app let willQuitApp = false; // 控制退出方式 mainWindow.on('close', (e) => { if (willQuitApp) { ...

  10. 深入探究JVM之类加载与双亲委派机制

    @ 目录 前言 类的生命周期 加载 验证 准备 解析 初始化 案例一 案例二 案例三 案例四 类加载器 类加载器和双亲委派模型 破坏双亲委派模型 第一次 SPI Tomcat OSGI 总结 前言 前 ...