当前位置: 首页 > article >正文

【C++】深入理解 C++ 输入输出同步机制:为什么 cin/cout 没有 scanf/printf 快?




在这里插入图片描述



一、同步机制

  • C++ 的 cincout:
    • cincout 默认是同步的,这意味着它们会与 C 语言的标准输入输出流(stdinstdout)保持同步。这种同步机制可以防止多个线程之间的冲突,但也增加了额外的开销。
  • C 语言的 scanfprintf:
    • scanfprintf 没有类似的同步机制,默认情况下是异步的,因此在多线程环境下需要注意同步问题。


二、为什么要同步:

  1. 共享缓冲区
    • cincout 使用的缓冲区与 stdinstdout 使用的缓冲区是相同的。这意味着无论你使用哪种方式读写数据,都会影响到同一个缓冲区。
  2. 顺序保证
    • 输入输出操作的顺序得到保证。例如,如果你先用 printf 输出一条消息,再用 cout 输出另一条消息,这两条消息会按照预期的顺序输出,不会交错。
  3. 避免冲突
    • 避免了在同一程序中混合使用 C++ 流和 C 语言函数时可能出现的冲突。例如,如果你在一个线程中使用 cin 读取数据,而在另一个线程中使用 scanf 读取数据,同步机制确保这两个操作不会相互干扰。


三、如果关闭同步机制会如何:

关闭同步机制后,cincout 不再与 stdinstdout 保持同步,这可能导致以下几种情况:

1、输出顺序不保证

  • 如果你在同一个程序中混合使用 coutprintf,输出的顺序可能不再按预期进行。例如,cout 的输出可能会覆盖 printf 的输出,或者两者之间的顺序可能会变得混乱。

  • 当同步机制关闭后,coutprintf 的缓冲区管理变得独立。每个 cout 调用和 printf 调用可能会在不同的时间点刷新缓冲区,导致输出顺序变得不可预测。

  • 举个例子:

    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    int main() {
        // 关闭同步机制
        ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
        cout << "Hello from C++\n";
        printf("Hello from C\n");
        
        cout << "Goodbye from C++\n";
        printf("Goodbye from C\n");
    
        return 0;
    }
    

如果没有关闭同步,输出顺序应该是这样的:

Hello from C++
Hello from C
Goodbye from C++
Goodbye from C

但在关闭同步机制后,实际输出可能会变得不可预测:

可能是这样

Hello from C++
Goodbye from C++
Hello from C
Goodbye from C

也可能是

Hello from C
Hello from C++
Goodbye from C
Goodbye from C++


2、输入冲突

  • 如果你在同一个程序中混合使用 cinscanf,可能会导致输入冲突。例如,cin 可能会读取 scanf 留下的缓冲区内容,或者反之亦然。


四、进一步理解细节:

1、两个输出缓冲区,导致输出顺序不同

关闭同步机制后,cin 和 cout 以及 scanf 和 printf 的缓冲区管理互相独立,各自有各自的一个缓冲区,而它们仍然共享同一个底层输入输出设备,这就说明一个程序内两个缓冲区对应着一个底层输入输出设备。

关系演示:关闭同步 ——> 两套输入输出的缓冲区管理独立 ——> 各自有一个缓冲区 ——> 两者共享同一个底层输入输出设备 == 一个程序内两个缓冲区对应着一个底层输入输出设备

就是因为关闭同步后,两者使用独立的缓冲区管理机制与各自独立的缓冲区,因此两者的刷新输出数据时机就各自管理,因此 输出顺序无法保证相同,就会出现顺序不一致的现象


2、一个输入缓冲区,导致读取数据错乱

在关闭同步机制后,尽管 cinscanf 使用独立的缓冲区管理机制,各自独立有一套输出缓冲区,但它们仍然共享同一个底层的输入缓冲区,即标准输入缓冲区。这个标准输入缓冲区是由操作系统管理的,用于暂存从输入设备(如键盘)接收到的数据。

用户通过键盘输入数据。

这些数据首先被暂存到操作系统的内核缓冲区中。内核缓冲区是操作系统内部用于暂存输入数据的区域。

当用户按下回车键时,内核缓冲区中的数据被刷新到用户层面的标准输入缓冲区。

cinscanf 就在这个用户层面的标准输入缓冲区中读取数据


(1)输入冲突的具体原因

  1. 标准输入缓冲区的残留数据
    • scanf 读取数据时,它可能不会完全清空标准输入缓冲区。剩余的数据可能会被后续的 cin 读取。
    • 同样,当 cin 读取数据时,它可能不会完全清空标准输入缓冲区,剩余的数据可能会被后续的 scanf 读取。
    • 例如用户通过键盘输入这样一条字符串到缓冲区:hello worldcin 遇到空格停止读取,当通过 cin 读取时,会将 hello 读取到,而将 world 留在缓冲区中,而 cin 又不会主动清空标准输入缓冲区,则剩余的数据可能会被后续的 scanf 读取。
  2. 独立的缓冲区管理
    • 因为管理机制独立,读取缓冲区的时机也不一致了:cinscanf 使用独立的缓冲区管理机制,它们各自决定何时从标准输入缓冲区中读取数据。


五、关闭同步机制

ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);

这行代码可以关闭 cincout 与 C 语言标准输入输出流的同步机制,并解除 cincout 之间的绑定,从而显著提高性能。

注意关闭了同步,就要注意尽量不要同时使用 cin/cout 和 scanf/printf



http://www.kler.cn/a/377511.html

相关文章:

  • MySQL查询where中包含多个in条件问题
  • 求平面连接线段组成的所有最小闭合区间
  • JeecgBoot入门
  • 【Android】Gradle 7.0+ 渠道打包配置
  • 文心一言 VS 讯飞星火 VS chatgpt (383)-- 算法导论24.5 3题
  • 高德地图美食
  • Java: 遍历 Map
  • Ubuntu编译linux内核指南(适用阿里云、腾讯云等远程服务器;包括添加Android支持)
  • golang有序map
  • vue3 + ts + element-plus 二次封装 el-table
  • ✨ Midjourney中文版:创意启航,绘梦无界 ✨
  • Harmony NEXT - AlphabetIndexer实现联系人字母索引
  • 密码学简介
  • Python入门:如何掌控多线程数量
  • 【OD-支持在线评测】智能驾驶(200分)
  • 无人机之自动控制原理篇
  • oracle-函数-NULLIF (expr1, expr2)的妙用
  • Vuestic 数据表格 使用demo
  • HTML前端页面设计静态网站
  • [NOIP2008 普及组] 排座椅
  • 【Redis:原理、架构与应用】
  • 中阳量化交易模型的探索与发展:科技引领金融未来
  • 东方娱乐周刊
  • 注册页面设计(表单基础)
  • 【机器学习】机器学习与成像技术:开启智能视觉的新篇章
  • Zypher Research:服务器抽象叙事,GameFi 赛道的下一个热点?