在C++ 98中写一个单独的字符串来cout"线程安全"

for*_*818 3 c++ multithreading c++98

我知道在C++ 98中严格说来没有什么是线程安全的,因为在标准中,在C++ 11之前没有线程.但是,在实践中,早在C++ 11之前就已经在C++中使用了线程.

所以让我们两个pthreads同时称之为:

void printSomething(){
    std::cout << "something\n";
}
Run Code Online (Sandbox Code Playgroud)

什么可以导致两个输出交错?或者我会在实践中总是两行包含"某些东西"吗?

我问这个是因为这个问题让我很惊讶,我发现这个答案说明:

...在C++ 11中,std :: cout是线程安全的.

这个答案提供了一个例子

std::cout << "aaaaaaaaaa" << "bbbbbbbbbb";
Run Code Online (Sandbox Code Playgroud)

并得出结论,在C++ 98中,执行此操作的两个线程可能具有交错输出,但我找不到任何关于两个线程operator<<只调用一次的内容.

Bee*_*ope 6

正如评论中指出的那样,C++ 98在标准中没有提供任何线程概念,所以你不能通过参考标准来回答这个问题.

然而,任何旨在与线程一起使用的合理的C++ 98 实现(即,大多数在某些嵌入式市场之外)几乎肯定会为"普通"用例提供或多或少的安全性 std::cout.毕竟,使用该流进行输出是非常常见的,并且在线程程序中跨线程使用它也将非常常见.

不幸的是,这与你可以说具体的一样多.特别要注意的是,我甚至没有以特定的方式定义 "安全".至少你可能期望它不会崩溃,破坏你的程序或"凭空"产生输出.除此之外,它取决于.

它取决于什么

下一步是检查实现本身.希望你有源1!

通常,您会发现实现具有一些与线程无关的代码(例如,复制到堆栈本地缓冲区),并在某些时候锁定,然后操纵std::cout对象内的共享状态.锁定的时间和地点很重要.

单字符串

例如,人们希望两个线程上的单个项的输出,如std::cout << "AAAA"线程1和std::cout << "BBBB"线程2上的输出至少会导致"非交错"输出AAAABBBB或者BBBBAAAA从不BBAAAABB或类似的输出.对于以某种方式缓冲的实现,这可能不是真的!例如,实现可以锁定,然后尽可能多地复制到内部缓冲区中,如果已满,则输出,然后解锁并再次继续.

请记住,即使std::cout对象完全被锁定(即,基本上整个operator<<(const char *)是在锁定下运行),当与其他写入的代码同时运行时,您可能会看到甚至单个字符串输出的交错,stdout但是通过除了std::cout- 如printf().

在这种情况下,C/C++运行时通常不会分享任何锁,你会依赖于底层的锁定write()由操作系统提供的电话型.尽管此调用是完全锁定且线程安全的,但它可能不会一次性写入整个字符串(这很常见,例如,当中间缓冲区填满时,例如写入管道时).

多个运算符

流接口的主要问题是多个运算符.类似的东西std::cout << "AAA" << "BBB" ...几乎总是变成对共享std::cout对象的多次调用.如果没有外部锁定,这些可以与其他线程以任何方式自由交错.至关重要的是,这几乎意味着流操纵iomanip器无法安全使用.类似的东西std::cout << std::hex << 123 << std::dec会全局修改流,而其他一些不想hex输出的线程可能会在错误的时间运行并且无论如何都会得到它.此外,一个事实,即流格式状态和标志可以在改变的操作可能会产生一些奇怪的,奇妙的或彻头彻尾的可怕结果.


1当然,源代码可用于开源运行时,如libstc ++(由gcc使用)和libc ++(在大多数非Linux平台上由LLVM默认使用).Microsoft似乎也在为其C运行时提供源代码.