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++编程中的多线程编程基本概念的更多相关文章

  1. JavaEE开发之Spring中的多线程编程以及任务定时器详解

    上篇博客我们详细的聊了Spring中的事件的发送和监听,也就是常说的广播或者通知一类的东西,详情请移步于<JavaEE开发之Spring中的事件发送与监听以及使用@Profile进行环境切换&g ...

  2. Java中的 多线程编程

    Java 中的多线程编程 一.多线程的优缺点 多线程的优点: 1)资源利用率更好2)程序设计在某些情况下更简单3)程序响应更快 多线程的代价: 1)设计更复杂虽然有一些多线程应用程序比单线程的应用程序 ...

  3. Python中的多线程编程,线程安全与锁(二)

    在我的上篇博文Python中的多线程编程,线程安全与锁(一)中,我们熟悉了多线程编程与线程安全相关重要概念, Threading.Lock实现互斥锁的简单示例,两种死锁(迭代死锁和互相等待死锁)情况及 ...

  4. 每天进步一点点——论fork()函数与Linux中的多线程编程

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/27316803 一.fork()函数     在操作系统的基本概念中进程是程序的一次运行,且是 ...

  5. Qt中的多线程编程

    http://www.ibm.com/developerworks/cn/linux/l-qt-mthrd/ Qt 作为一种基于 C++ 的跨平台 GUI 系统,能够提供给用户构造图形用户界面的强大功 ...

  6. C语言中的多线程编程

    很久很久以前,我对C语言的了解并不是很多,我最早听说多线程编程是用Java,其实C语言也有多线程编程,而且更为简单.方便.强大.下面就让我们简单领略一下Unix C语言环境下的多线程编程吧! 下面先看 ...

  7. Java的并发编程中的多线程问题到底是怎么回事儿?

    在我之前的一篇<再有人问你Java内存模型是什么,就把这篇文章发给他.>文章中,介绍了Java内存模型,通过这篇文章,大家应该都知道了Java内存模型的概念以及作用,这篇文章中谈到,在Ja ...

  8. .NET 4中的多线程编程之一:使用Task(转载)

    原文地址:http://www.cnblogs.com/yinzixin/archive/2011/11/04/2235233.html .NET 4为了简化多线程编程,提供了System.Threa ...

  9. Python中的多线程编程,线程安全与锁(一)

    1. 多线程编程与线程安全相关重要概念 在我的上篇博文 聊聊Python中的GIL 中,我们熟悉了几个特别重要的概念:GIL,线程,进程, 线程安全,原子操作. 以下是简单回顾,详细介绍请直接看聊聊P ...

  10. Android中的多线程编程(一)附源代码

    Android中多线程编程:Handler类.Runnable类.Thread类之概念分析 1.Handler类: Handler是谷歌封装的一种机制:能够用来更新UI以及消息的发送和处理.Handl ...

随机推荐

  1. webpack笔记-webpack基础用法(二)

    webpack 本质上是一个打包工具,它会根据代码的内容解析模块依赖,帮助我们把多个模块的代码打包. 一切文件:JavaScript.CSS.SCSS.图片.模板,在 Webpack 眼中都是一个个模 ...

  2. SQL Server – Temporal Table 时态表

    前言 之前写过一篇, 但那个时候还没有开始用, 现在是要用了, 所以翻新一下呗. SQL server temporal table 学习笔记 主要参考: 官网 Temporal tables [译] ...

  3. Figma 学习笔记 – Prototype

    挺简单的, 只要知道它有什么, 基本上就会用了 监听 Event Type 监听 Callback Action 过度 Animation Frame Scrolling

  4. freemarker实现导出word复选框可点击效果

    记一次java导出word文档,导出的word文档里包含复选框并且能点击,一开始做了个输出字符的,比如这样: □,然而并不能满足需求,网上找了一大堆也都是这种的. 正文开始: 先在word中添加复选框 ...

  5. 东方通TongWeb7部署SuperMap iServer War包

    一.软件版本 操作系统: CentOS 7.5.1804 JDK:1.8_201 东方通:TongWeb7.0.4.2 SuperMap iServer:10.2 二.东方通TongWeb7部署流程 ...

  6. UEFI原理与编程(二)

    系统表 对UEFI应用程序和驱动程序开发人员来讲,系统表是最重要的数据结构之一,它是用户空间通往内核空间的通道.有了它,UEFI应用程序和驱动才可以访问UEFI内核.硬件资源和I/O设备. 1 在应用 ...

  7. USB-A, Micro, lightning and USB-C

  8. /proc/buddyinfo

    在应用程序设计过程中,内存是很重要的资源,而计算机主机的内存资源时有限的.一般而言我们可以申请到的内存是有限的,并不是想申请多大就有多大就可以申请多大的./proc/buddyinfo文件里,就记录着 ...

  9. qemu的使用

    一.QEMU的运行模式 直接摘抄自己<揭秘家用路由器0day漏洞挖掘技术>,网上查了一下也没有找到令人满意的QEMU的使用说明,就采用这本书上的介绍.如果后期能够找到比较满意的QEMU的使 ...

  10. 简述 JavaScript脚本的执行原理?

    js 是一种动态 . 弱类型 . 基于原型的语言 ,通过浏览器可以直接执行: 当浏览器遇到 <script></script>标记时 , 会执行标记之间的js 代码:然后js ...