Linux C/C++编程中的多线程编程基本概念
8.2.1 操作系统和多线程
要在应用程序中实现多线程,必须有操作系统的支持。Linux 32位或64位操作系统对应用程序提供了多线程的支持,所以Windows NT/2000/XP/7/8/10是多线程操作系统。根据进程与线程的支持情况,可以把操作系统大致分为如下几类:
(1)单进程、单线程,MS-DOS大致是这种操作系统。
(2)多进程、单线程,多数UNIX(及类UNIX的Linux)是这种操作系统。
(3)多进程、多线程,Win32(Windows NT/2000/XP/7/8/10等)、Solaris 2.x和OS/2都是这种操作系统。
(4)单进程、多线程,VxWorks是这种操作系统。
具体到Linux C++的开发环境,它提供了一套POSIX API函数来管理线程,用户既可以直接使用这些POSIX API函数,也可以使用C++自带的线程类。作为一名Linux C++开发者,这两者都应该会使用,因为在Linux C++程序中,这两种方式都有可能出现。
8.2.2 线程的基本概念
现代操作系统大多支持多线程概念,每个进程中至少有一个线程,所以即使没有使用多线程编程技术,进程也含有一个主线程,所以也可以说,CPU中执行的是线程,线程是程序的最小执行单位,是操作系统分配CPU时间的最小实体。一个进程的执行说到底是从主线程开始的,如果需要,可以在程序任何地方开辟新的线程,其他线程都是由主线程创建的。一个进程正在运行,也可以说是一个进程中的某个线程正在运行。一个进程的所有线程共享该进程的公共资源,比如虚拟地址空间、全局变量等。每个线程也可以拥有自己私有的资源,如堆栈、在堆栈中定义的静态变量和动态变量、CPU寄存器的状态等。
线程总是在某个进程环境中创建的,并且会在这个进程内部销毁。线程和进程的关系是:线程是属于进程的,线程运行在进程空间内,同一进程所产生的线程共享同一内存空间,当进程退出时,该进程所产生的线程都会被强制退出并清除。线程可与属于同一进程的其他线程共享进程所拥有的全部资源,但是其本身基本上不拥有系统资源,只拥有一点在运行中必不可少的信息(如程序计数器、一组寄存器和线程栈,线程栈用于维护线程在执行代码时需要的所有函数参数和局部变量)。
相对于进程来说,线程所占用的资源更少。比如创建进程,系统要为它分配很大的私有空间,占用的资源较多;而对于多线程程序来说,由于多个线程共享一个进程地址空间,因此占用的资源较少。此外,进程间切换时,需要交换整个地址空间,而线程间切换时,只是切换线程的上下文环境,因此效率更高。在操作系统中引入线程带来的主要好处是:
(1)在进程内创建、终止线程比创建、终止进程要快。
(2)同一进程内线程间的切换比进程间的切换要快,尤其是用户级线程间的切换。
(3)每个进程具有独立的地址空间,而该进程内的所有线程共享该地址空间,因此线程的出现可以解决父子进程模型中子进程必须复制父进程地址空间的问题。
(4)线程对解决客户/服务器模型非常有效。
虽然多线程给应用开发带来了不少好处,但并不是所有情况下都要去使用多线程,要具体问题具体分析。通常在下列情况下可以考虑使用多线程:
(1)应用程序中的各任务相对独立。
(2)某些任务耗时较多。
(3)各任务有不同的优先级。
(4)一些实时系统应用。
值得注意的是,一个进程中的所有线程共享它们父进程的变量,但同时每个线程可以拥有自己的变量。
8.2.3 线程的状态
一个线程在从创建到结束这一生命周期中,总是处于下面4个状态中的一个。
1)就绪态
线程能够运行的条件已经满足,只是在等待处理器(处理器要根据调度策略来把就绪态的线程调度到处理器中运行)。处于就绪态的原因可能是线程刚刚被创建(刚创建的线程不一定马上运行,一般先处于就绪态),也可能是刚刚从阻塞状态中恢复,还可能是因被其他线程抢占而处于就绪态。
2)运行态
运行态表示线程正在处理器中运行,正占用着处理器。
3)阻塞态
由于在等待处理器之外的其他条件而无法运行的状态叫作阻塞态。这里的其他条件包括I/O操作、互斥锁的释放、条件变量的改变等。
4)终止态
终止态就是线程的线程函数运行结束或被其他线程取消后处于的状态。处于终止态的线程虽然已经结束了,但它所占资源还没有被回收,而且还可以被重新复活。我们不应该长时间让线程处于这种状态,线程处于终止态后应该及时进行资源回收,下面会讲到如何回收。
8.2.4 线程函数
线程函数就是线程创建后进入运行态后要执行的函数。执行线程说到底就是执行线程函数。这个函数是我们自定义的,然后在创建线程时把我们的函数作为参数传入线程创建函数。
同理,中断线程的执行就是中断线程函数的执行,以后再恢复线程的时候,就会在前面线程函数暂停的地方继续执行下面的代码。结束线程也就不再运行线程函数。线程的函数可以是一个全局函数或类的静态函数,比如在POSIX线程库中,它通常这样声明:
void *ThreadProc (void *arg);
其中,参数arg指向要传给线程的数据,这个参数是在创建线程的时候作为参数传入线程创建函数中的。函数的返回值应该表示线程函数运行的结果:成功还是失败。注意函数名ThreadProc可以是自定义的函数名,这个函数是用户自己先定义好,然后由系统来调用。
8.2.5 线程标识
既然句柄是用来标识线程对象的,那线程本身用什么来标识呢?在创建线程的时候,系统会为线程分配唯一的ID作为线程的标识,这个ID从线程创建开始就存在,一直伴随着线程的结束才消失。线程结束后,该ID就自动不存在,我们不需要去显式清除它。
通常线程创建成功后会返回一个线程ID。
8.2.6 C++多线程开发的两种方式
在Linux C++开发环境中,通常有两种方式来开发多线程程序:一种是利用POSIX多线程API函数来开发多线程程序,另一种是利用C++自带线程类来开发多线程程序。这两种方式各有利弊。前一种方法比较传统,后一种方法比较新,是C++11推出的方法。为何C++程序员也要熟悉POSIX多线程开发呢?这是因为C++11以前,在C++里面使用多线程一般都是利用POSIX多线程API,或者把POSIX多线程API封装成类,再在公司内部供大家使用。因此,一些老项目都是和POSIX多线程库相关的,这也使得我们必须熟悉它,因为很可能进入公司后会要求维护以前的程序代码。而C++自带线程类很可能在以后开发新的项目时会用到。总之,技多不压身。
本文节选自《Linux C与C++一线开发实践(第2版)》,获出版社和作者授权发布。
Linux C/C++编程中的多线程编程基本概念的更多相关文章
- JavaEE开发之Spring中的多线程编程以及任务定时器详解
上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...
- Java中的 多线程编程
Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...
- Python中的多线程编程,线程安全与锁(二)
在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...
- 每天进步一点点——论fork()函数与Linux中的多线程编程
转载请说明出处:http://blog.csdn.net/cywosp/article/details/27316803 一.fork()函数 在操作系统的基本概念中进程是程序的一次运行,且是 ...
- Qt中的多线程编程
http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...
- C语言中的多线程编程
很久很久以前,我对C语言的了解并不是很多,我最早听说多线程编程是用Java,其实C语言也有多线程编程,而且更为简单.方便.强大.下面就让我们简单领略一下Unix C语言环境下的多线程编程吧! 下面先看 ...
- Java的并发编程中的多线程问题到底是怎么回事儿?
在我之前的一篇<再有人问你Java内存模型是什么,就把这篇文章发给他.>文章中,介绍了Java内存模型,通过这篇文章,大家应该都知道了Java内存模型的概念以及作用,这篇文章中谈到,在Ja ...
- .NET 4中的多线程编程之一:使用Task(转载)
原文地址:http://www.cnblogs.com/yinzixin/archive/2011/11/04/2235233.html .NET 4为了简化多线程编程,提供了System.Threa ...
- Python中的多线程编程,线程安全与锁(一)
1. 多线程编程与线程安全相关重要概念 在我的上篇博文 聊聊Python中的GIL 中,我们熟悉了几个特别重要的概念:GIL,线程,进程, 线程安全,原子操作. 以下是简单回顾,详细介绍请直接看聊聊P ...
- Android中的多线程编程(一)附源代码
Android中多线程编程:Handler类.Runnable类.Thread类之概念分析 1.Handler类: Handler是谷歌封装的一种机制:能够用来更新UI以及消息的发送和处理.Handl ...
随机推荐
- 搭建ipv6并发代理池
声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 学习目标 ounter(l ...
- Mininet安装记录
安装环境: Ubuntu虚拟机版本:14.04 Mininet版本:2.3.1b1 1.更改软件镜像源 在设置中进行如下操作: 选择国内的镜像站点,如阿里云. 点击关闭后,在弹出的窗口中点击重新载入, ...
- Vue3.5中解构props,让父子组件通信更加丝滑
前言 在Vue3.5版本中响应式 Props 解构终于正式转正了,这个功能之前一直是试验性的.这篇文章来带你搞清楚,一个String类型的props经过解构后明明应该是一个常量了,为什么还没丢失响应式 ...
- [C++] Rander
注 这个Rander对单个数据的平均分散不太优秀,但是获取大量数据十分平均 当前版本 2.0 for Windows 功能 int rander::reset() 按默认大小重置随机数序列,返回默认大 ...
- 微信小程序上拉加载
下面是一个示例,在个人使用的过程中按自己需求进行更改 创建一个DataController控制器 php artisan make:controller DataController 创建一个Data ...
- neo4j 统计多个节点之间关系总和的写法-包含多个collect合并成一个并去重操作
在使用neo4j数据库时,会遇到计算与指定节点产生关联的数量统计需求,例如指定6个节点1,2,3,4,5,6需要找出与这6个节点中4个节点有关联的节点(要求排除这6个节点的数据) 先看实现查询语句: ...
- Android Studio自带Profiler工具内存泄露分析步骤
1.运行需要检测内存泄露的程序 这里以"com.example.opengltest"程序为例. 2.点击Profiler按钮 3.点击SESIONS "+"号 ...
- ArgoWorkflow教程(六)---无缝实现步骤间参数传递
之前我们分析了,Workflow.WorkflowTemplate .template 3 者之间如何传递参数. 本文主要分析同一个 Workflow 中的不同 step 之间实现参数传递,比如将上一 ...
- 2021年1月国产数据库排行榜:OceanBase重回前三,TDSQL增长趋势最强劲!
墨天轮国产数据库排行榜新年第一期已发布.2021年1月份排行榜前三甲依次为 TiDB .DaMeng.OceanBase .PingCAP TiDB 稳居冠军的宝座,短时间内难以撼动,开源的商业数据库 ...
- Termux 使用笔记
第一次安装完成后 发现这里面啥都没有 所以 更新源 apt update 也做不到 这是因为 源是国外 由于有墙 所以连接不上 下面这个命令可以 切换更新源 termux-change-repo 切换 ...