两个不同流上的operator<<(ostream&, obj) 线程安全吗?

2024-04-06

#include <iostream>
#include <sstream>
#include <thread>

using namespace std;

int main()
{
    auto runner = []() {
        ostringstream oss;
        for (int i=0; i<100000; ++i)
            oss << i;
    };

    thread t1(runner), t2(runner);
    t1.join(); t2.join();
}

在g++6.2.1中编译上面的代码,然后运行它valgrind --tool=helgrind ./a.out。赫尔格琳德会抱怨:

==5541== ----------------------------------------------------------------
==5541== 
==5541== Possible data race during read of size 1 at 0x51C30B9 by thread #3
==5541== Locks held: none
==5541==    at 0x4F500CB: widen (locale_facets.h:875)
==5541==    by 0x4F500CB: widen (basic_ios.h:450)
==5541==    by 0x4F500CB: fill (basic_ios.h:374)
==5541==    by 0x4F500CB: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==5541==    by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12)
==5541==    by 0x4011F7: void std::_Bind_simple<main::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391)
==5541==    by 0x401194: std::_Bind_simple<main::{lambda()#1} ()>::operator()() (functional:1380)
==5541==    by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1} ()> >::_M_run() (thread:197)
==5541==    by 0x4EF858E: execute_native_thread_routine (thread.cc:83)
==5541==    by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5541==    by 0x56E7453: start_thread (in /usr/lib/libpthread-2.24.so)
==5541==    by 0x59E57DE: clone (in /usr/lib/libc-2.24.so)
==5541== 
==5541== This conflicts with a previous write of size 8 by thread #2
==5541== Locks held: none
==5541==    at 0x4EF3B1F: do_widen (locale_facets.h:1107)
==5541==    by 0x4EF3B1F: std::ctype<char>::_M_widen_init() const (ctype.cc:94)
==5541==    by 0x4F501B7: widen (locale_facets.h:876)
==5541==    by 0x4F501B7: widen (basic_ios.h:450)
==5541==    by 0x4F501B7: fill (basic_ios.h:374)
==5541==    by 0x4F501B7: std::ostream& std::ostream::_M_insert<long>(long) (ostream.tcc:73)
==5541==    by 0x400CD0: main::{lambda()#1}::operator()() const (43.cpp:12)
==5541==    by 0x4011F7: void std::_Bind_simple<main::{lambda()#1} ()>::_M_invoke<>(std::_Index_tuple<>) (functional:1391)
==5541==    by 0x401194: std::_Bind_simple<main::{lambda()#1} ()>::operator()() (functional:1380)
==5541==    by 0x401173: std::thread::_State_impl<std::_Bind_simple<main::{lambda()#1} ()> >::_M_run() (thread:197)
==5541==    by 0x4EF858E: execute_native_thread_routine (thread.cc:83)
==5541==    by 0x4C31A04: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==5541==  Address 0x51c30b9 is 89 bytes inside data symbol "_ZN12_GLOBAL__N_17ctype_cE"

看来两个线程都调用了locale_facet.h:widen这导致了数据竞争,因为该函数中似乎没有同步,即使operator <<被调用于两个不同的ostringstream目的。所以我想知道这是否真的是一场数据竞赛还是只是误报helgrind.


Update:我承认在回答之前我没有完全阅读问题,所以我自己进行了研究。

TL;DR

这里有可变的成员变量可能会导致竞争条件。 ios 每个语言环境都有静态变量,这些静态变量延迟加载(首次需要时初始化)查找表。如果您想避免并发问题,请确保在加入线程之前初始化区域设置,以便任何线程操作只能读取这些查找表。

细节

当一个流被初始化时,它会填充一个指针,该指针加载到语言环境的正确 ctype 中(请参阅 _M_ctype):https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_ios.h#L273 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/basic_ios.h#L273

该错误指的是:https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/locale_facets.h#L875 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/locale_facets.h#L875

如果两个线程同时初始化相同的区域设置,这可能是竞争条件。

这应该是线程安全的(尽管它仍然可能会出错):

// Force ctype to be initialized in the base thread before forking
ostringstream dump;
dump << 1;

auto runner = []() {
    ostringstream oss;
    for (int i=0; i<100000; ++i)
        oss << i;
};

thread t1(runner), t2(runner);
t1.join(); t2.join();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

两个不同流上的operator<<(ostream&, obj) 线程安全吗? 的相关文章

  • 为什么在连接两个字符串时 Python 比 C 更快?

    目前我想比较 Python 和 C 用来处理字符串的速度 我认为 C 应该比 Python 提供更好的性能 然而 我得到了完全相反的结果 这是 C 程序 include
  • 计算 XML 中特定 XML 节点的数量

    请参阅此 XML
  • 代码 GetAsyncKeyState(VK_SHIFT) & 0x8000 中的这些数字是什么?它们是必不可少的吗?

    我试图在按下按键的简单动作中找到这些数字及其含义的任何逻辑解释 GetAsyncKeyState VK SHIFT 0x8000 可以使用哪些其他值来代替0x8000它们与按键有什么关系 GetAsyncKeyState 根据文档返回 如果
  • 函数参数的默认参数是否被视为该参数的初始值设定项?

    假设我有这样的函数声明 static const int R 0 static const int I 0 void f const int r R void g int i I 根据 dcl fct default 1 如果在参数声明中指
  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • C# 存档中的文件列表

    我正在创建一个 FileFinder 类 您可以在其中进行如下搜索 var fileFinder new FileFinder new string C MyFolder1 C MyFolder2 new string
  • 启动时的 Excel 加载项

    我正在使用 Visual C 创建 Microsoft Excel 的加载项 当我第一次创建解决方案时 它包含一个名为 ThisAddIn Startup 的函数 我在这个函数中添加了以下代码 private void ThisAddIn
  • 打破 ReadFile() 阻塞 - 命名管道 (Windows API)

    为了简化 这是一种命名管道服务器正在等待命名管道客户端写入管道的情况 使用 WriteFile 阻塞的 Windows API 是 ReadFile 服务器已创建启用阻塞的同步管道 无重叠 I O 客户端已连接 现在服务器正在等待一些数据
  • IQueryable 单元或集成测试

    我有一个 Web api 并且公开了一个端点 如下所示 api 假期 name name 这是 Web api 的控制器 get 方法 public IQueryable
  • 如何在 C 中安全地声明 16 位字符串文字?

    我知道已经有一个标准方法 前缀为L wchar t test literal L Test 问题是wchar t不保证是16位 但是对于我的项目 我需要16位wchar t 我还想避免通过的要求 fshort wchar 那么 C 不是 C
  • 为什么这个二维指针表示法有效,而另一个则无效[重复]

    这个问题在这里已经有答案了 这里我编写了一段代码来打印 3x3 矩阵的对角线值之和 这里我必须将矩阵传递给函数 矩阵被传递给指针数组 代码可以工作 但问题是我必须编写参数的方式如下 int mat 3 以下导致程序崩溃 int mat 3
  • 等待 IAsyncResult 函数直至完成

    我需要创建等待 IAsyncResult 方法完成的机制 我怎样才能做到这一点 IAsyncResult result contactGroupServices BeginDeleteContact contactToRemove Uri
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • 使 Guid 属性成为线程安全的

    我的一个类有一个 Guid 类型的属性 该属性可以由多个线程同时读写 我的印象是对 Guid 的读取和写入不是原子的 因此我应该锁定它们 我选择这样做 public Guid TestKey get lock testKeyLock ret
  • Unity:通过拦截将两个接口注册为一个单例

    我有一个实现两个接口的类 我想对该类的方法应用拦截 我正在遵循中的建议Unity 将两个接口注册为一个单例 https stackoverflow com questions 1394650 unity register two inter
  • 将数组作为参数传递

    如果我们修改作为方法内参数传递的数组的内容 则修改是在参数的副本而不是原始参数上完成的 因此结果不可见 当我们调用具有引用类型参数的方法时 会发生什么过程 这是我想问的代码示例 using System namespace Value Re
  • 堆栈是向上增长还是向下增长?

    我在 C 中有这段代码 int q 10 int s 5 int a 3 printf Address of a d n int a printf Address of a 1 d n int a 1 printf Address of a
  • 中断连接套接字

    我有一个 GUI 其中包含要连接的服务器列表 如果用户单击服务器 则会连接到该服务器 如果用户单击第二个服务器 它将断开第一个服务器的连接并连接到第二个服务器 每个新连接都在一个新线程中运行 以便程序可以执行其他任务 但是 如果用户在第一个
  • 是否可以在不连接数据库的情况下检索 MetadataWorkspace?

    我正在编写一个需要遍历实体框架的测试库MetadataWorkspace对于给定的DbContext类型 但是 由于这是一个测试库 我宁愿不连接到数据库 它引入了测试环境中可能无法使用的依赖项 当我尝试获取参考时MetadataWorksp
  • 不区分大小写的字符串比较 C++ [重复]

    这个问题在这里已经有答案了 我知道有一些方法可以进行忽略大小写的比较 其中涉及遍历字符串或一个good one https stackoverflow com questions 11635 case insensitive string

随机推荐