前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[C++并发编程] 1. 并发编程入门

[C++并发编程] 1. 并发编程入门

作者头像
轻舞飞扬SR
发布2021-02-24 10:59:34
4570
发布2021-02-24 10:59:34
举报
文章被收录于专栏:Visual Codex

本系列记录学习C++并发编程过程中的一些归纳总结的笔记。

并发编程

对并发编程最简单地说明就是CPU同时处理两个或更多独立的任务。

那么我们为什么需要并发编程呢?举个简单的例子,如果你想开发一个界面应用程序,这个程序需要若干个存有100万个数据的CSV文件进行处理,然后将处理完的数据写入到另外的文件,那么这个程序的任务就可以分为三个小部分:导入CSV文件,处理数据,写出数据,界面显示进度(导入/写出),如果不使用并发,那么需要先等所有的CSV文件导入后,然后处理数据,再处理数据的同时更新数据处理的进度,然后处理下一个数据之前需要等待当前数据写入到文件,这样的话,在处理一个任务的时候,另外的任务会处于“僵死”的状态。比如处理数据的时候,界面上的按钮将无法使用,点击界面上控件的时候,数据将无法被处理。

如果使用并发编程,那这些问题将得到解决,读取CSV文件,处理数据,处理页面响应,写出到文件都可以单独进行,这样既处理好了任务,与用户的实时交互也变得友好了。

那么,开始并发之前,有两个概念需要搞清楚,即“多处理器”和“多核”。多处理器是指在一台电脑上存在有多个物理CPU,这样的配置即使是现在也基本上只会在服务器上使用;而多核,也可以说多核处理器,是指只有一个物理CPU,但是在这个CPU中做了多个核心,每个核心就相当于一个个的小CPU,这样的多核心CPU普遍存在于我们现在的普通家用计算机中,可以在计算机的设备管理器中查看自己的电脑是几核的计算机。

在设备管理器中查看处理器信息
在设备管理器中查看处理器信息

或者也可以在程序中进行查看

代码语言:javascript
复制
#include <iostream>
#include <thread>

int main()
{
    std::thread t();
    std::cout << t.hardware_concurrency() << std::endl;
    t.join();
	return 0;
}
// 程序会打印出 8 ,意味着运行该程序的计算机搭载的是8核CPU。

在多处理器机中,每个处理器处理着不同的任务,不互相干扰,可以实现真正意义上的并发,但是在早期的单处理器时代,只有一个单核CPU,实现的并发只是表面上看起来并发,实际上,要执行的每个任务都被分配了细微到人感受不到的微小时间片,反过来说,在每个时间片,执行的任务可能是不同的,相邻的两个时间片的任务不同时,则中间还会存在一定的任务切换时间。

双核和单核
双核和单核

上图中绿色和红色分别代表一项任务,如果这两项任务由一个双核处理器来完成的话,那么可以每一个核心处理一个任务,互补干扰;如果是一个单核处理器来完成的话,那么为了能使两个任务“同时”进行,则需要将时间分成很小的片,每个时间片交替处理两个任务,在单核处理器处理过程中还存在灰色的小时间片,这是在两个任务之间切换(task switching)所花的时间。

多进程与多线程

讲并发,一般会想到两种,一种是多进程并发,一种是多线程并发。

首先,什么是进程?我们自己写的可以运行的代码,保存在电脑的硬盘上,叫做程序,但是,一旦一个可执行的程序跑起来了,那么这段代码就叫做一个进程,简单说,进程就是在计算机上运行起来的代码。比如说IE浏览器,Word文档等,这就是不同的进程,但是它们可以同时运行,这就是多进程,进程与进程之间可以通过多种方法传递信息,如信号,socket,文件,管道等,这就是进程间通信。

线程是一种比进程更小一级的单位,可以理解为进程中的一个个子任务,一个进程可以包含多个线程,每个线程也是独立运行的,但是与多进程不同的是,线程与线程之间共享地址空间,且所有线程能访问到大部分数据。

进程与线程的对比
进程与线程的对比

多线程相比起多进程,共享地址空间,在切换任务的时候,多线程花费更少的时间成本,操作系统开销更小,但是由于数据指令共享地址空间,也会带来复杂度提高的缺点,处理更加的复杂。

接下来我主要针对多线程并发进行了学习,多进程学习以后再在别的博客里再写。

Hello World

一个简单的C++多线程程序

代码语言:javascript
复制
#include <iostream>
#include <thread>

void helloworld()
{
    std::cout << "Hello World" << std::endl;
}

int main()
{
    std::thread t(helloworld);
    t.join();
    return 0;
}

上面的程序与普通的C++ Hello World 程序不同的地方在于

  • 头文件添加了 <thread> 头文件,<thread> 头文件包含了 thread类的定义和相应成员函数的定义,如 join(), get_id()等,因此在程序中可以使用 std::thread 来定义线程类。
  • 使用std::thread 来定义线程类,定义线程类的时候,可以附带参数,这个例子中的参数 helloworld 为线程起始函数(initial function),既线程启动时,需要从哪个函数开始执行。

其实,一个进程启动时,会有一个主线程启动(main thread),这个线程函数从程序开始自动生成,程序终止时自动消亡,所以main thread从main函数开始执行,即main函数为主线程的起始函数。

  • join()函数表示阻塞,阻塞的意思就是等待 t 线程执行完毕后,当前线程再继续执行,所以当main函数中遇到t.join()这语句时,主线程暂停执行,等待 t 线程执行完毕,打印出 Hello World后,再继续执行。

执行的流程图如下

主线程与子线程在有阻塞的情况下的执行顺序
主线程与子线程在有阻塞的情况下的执行顺序

总结

多线程编程优点:

  • 一个进程中的所有线程共享地址空间,全局变量,指针,引用都可以在线程之间传递,所以使用多线程开销远远小于多进程。
  • 比起多进程,多线程启动速度更快,更轻量级。

不足的地方:

  • 共享内存带来数据一致性问题。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/02/06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 并发编程
  • 多进程与多线程
  • Hello World
  • 总结
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档