第十二章 并发编程

如果逻辑控制流在时间上重叠,那么他们就是并发的。应用级并发在以下情况中发挥作用:

  1. 访问慢速I/O设备。
  2. 与人交互。
  3. 通过推迟工作以降低延迟。
  4. 服务多个网络客户端。
  5. 在多核机器上进行并行计算。

使用应用级并发的应用程序称为并发程序。现代操作系统提供了三种基本的构造并发程序的方法:

  • 进程。每个逻辑控制流都是一个进程,由内核来调度和维护。控制流使用显式的进程间通信(IPC)机制。

  • I/O多路复用。应用程序在一个进程的上下文中显式地调度他们自己的逻辑流。所有的流都共享同一个地址空间。

  • 线程。线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。

12.1 基于进程的并发编程

基于进程的并发服务器

  1. 使用SIGCHLD处理程序来回收僵死子进程的资源。

  2. 父进程必须关闭他们各自的connfd拷贝(已连接的描述符),避免存储器泄露。

  3. 因为套接字的文件表表项中的引用计数,直到父子进程的connfd都关闭了,到客户端的连接才会终止。

注意

1.父进程需要关闭它的已连接描述符的拷贝(子进程也需要关闭)

2.必须要包括一个SIGCHLD处理程序来回收僵死子进程的资源

3.父子进程之间共享文件表,但是不共享用户地址空间。

关于独立地址空间

  1. 优点:防止虚拟存储器被错误覆盖

  2. 缺点:开销高,共享状态信息才需要IPC机制

第二节 基于I/O多路复用的并发编程

使用select函数,要求内核挂起进程,只有在一个或多个I/O事件发生后,才将控制返回给应用程序。

int select(int n,fd_set *fdset,NULL,NULL,NULL);
返回已经准备好的描述符的非0的个数,若出错则为-1。

select函数处理类型为fd_set的集合,叫做描述符集合,看做一个大小为n位的向量:

bn-1,......,b1,b0

对描述符集合的处理方法:

  1. 分配他们

  2. 将一个此种类型的变量赋值给另一个变量

  3. 用FD_ZERO,FD_SET,FD_CLR和FD_ISSET宏指令来修改和检查他们。

基于I/O多路复用的并发事件驱动服务器

  1. I/O多路复用可以用作事件并发驱动程序的基础。

  2. 状态机:一组状态、输入事件、输出事件和转移。

  3. 自循环:同一输入和输出状态之间的转移。

I/O多路复用技术的优劣

  • 相比基于进程的设计给了程序员更多的对进程行为的控制,运行在单一进程上下文中,每个逻辑流都能访问全部的地址空间,在流之间共享数据很容易。

  • 编码复杂,随着并发粒度的减小,复杂性还会上升。粒度:每个逻辑流每个时间片执行的指令数量。

12.3 基于线程的并发编程

线程执行模型

每个进程开始生命周期时都是单一线程(主线程),在某一时刻创建一个对等线程,从此开始并发地运行,最后,因为主线程执行一个慢速系统调用,或者被中断,控制就会通过上下文切换传递到对等线程。

Posix线程

Posix线程是C语言中处理线程的一个标准接口,允许程序创建、杀死和回收线程,与对等线程安全的共享数据。

线程的代码和本地数据被封装在一个线程例程中,

创建线程

线程通过调用pthread_create来创建其他线程。

int pthread_create(pthread_t *tid,pthread_attr_t *attr,func *f,void *arg);
成功则返回0,出错则为非零

当函数返回时,参数tid包含新创建的线程的ID,新线程可以通过调用pthread_self函数来获得自己的线程ID。

pthread_t pthread_self(void);返回调用者的线程ID。

终止线程

一个线程是通过以下方式之一来终止的。

当顶层的线程例程返回时,线程会隐式地终止。

通过调用pthread_exit函数,线程会显式地终止

void pthread_exit(void *thread_return);

回收已终止的线程资源

线程通过调用pthread_join函数等待其他线程终止。

int pthread_join(pthread_t tid,void **thread_return);
成功则返回0,出错则为非零

分离线程

在任何一个时间点上,线程是可结合或可分离的。一个可结合的线程能够被其他线程收回其资源和杀死,在被回收之前,它的存储器资源是没有被释放的。分离的线程则相反,资源在其终止时自动释放。

int pthread_deacth(pthread_t tid);
成功则返回0,出错则为非零

初始化线程

pthread_once允许初始化与线程例程相关的状态。

pthread_once_t once_control=PTHREAD_ONCE_INIT;
int pthread_once(pthread_once_t *once_control,void (*init_routine)(void));
总是返回0

12.4 多线程程序中的共享变量

一个变量是共享的,当且仅当多个线程引用这个变量的某个实例。

一、线程存储器模型

  1. 每个线程都有自己独立的线程上下文,包括一个唯一的整数线程ID,栈、栈指针、程序计数器、通用目的寄存器和条件码。

  2. 寄存器是从不共享的,而虚拟存储器总是共享的。

  3. 各自独立的线程栈被保存在虚拟地址空间的栈区域中,并且通常是被相应的线程独立地访问的。

二、将变量映射到存储器

  1. 全局变量:定义在函数之外的变量

  2. 本地自动变量:定义在函数内部但是没有static属性的变量。

  • 本地静态变量:定义在函数内部并有static属性的变量。

三、共享变量

变量v是共享的——当且仅当它的一个实例被一个以上的线程引用。

12.5用信号量同步线程

共享变量的同时引入了同步错误,即没有办法预测操作系统是否为线程选择一个正确的顺序。

进度图

将n个并发线程的执行模型化为一条n维笛卡尔空间中的轨迹线,将指令模型化为从一种状态到另一种状态的转换。

信号量

  1. P(s):如果s是非零的,那么P将s减一,并且立即返回。如果s为零,那么就挂起这个线程,直到s变为非零。

  2. V(s):将s加一,如果有任何线程阻塞在P操作等待s变为非零,那么V操作会重启线程中的一个,然后该线程将s减一,完成他的P操作。

    信号量不变性:一个正确初始化了的信号量有一个负值。

信号量操作函数:

int sem_init(sem_t *sem,0,unsigned int value);//将信号量初始化为value
int sem_wait(sem_t *s);//P(s)
int sem_post(sem_t *s);//V(s)

使用信号量来实现互斥

  • 二元信号量(互斥锁):将每个共享变量与一个信号量s联系起来,然后用P(s)(加锁)和V(s)(解锁)操作将相应的临界区包围起来。

  • 禁止区:s<0,因为信号量的不变性,没有实际可行的轨迹线能够直接接触不安全区的部分

12.6 使用线程来提高并行性

并行程序的加速比通常定义为:

Sp=T1/Tp

其中,p为处理器核的数量,T为在p个核上的运行时间

12.7 其他并发问题

线程安全性

一个线程是安全的,当且仅当被多个并发线程反复的调用时,它会一直产生正确的结果。

四个不相交的线程不安全函数类以及应对措施:

不保护共享变量的函数——用P和V这样的同步操作保护共享变量

保持跨越多个调用的状态的函数——重写,不用任何static数据。

返回指向静态变量的指针的函数——①重写;②使用加锁-拷贝技术。

调用线程不安全函数的函数——参考之前三种

可重入性

当它们被多个线程调用时,不会引用任何共享数据。

1.显式可重入的:

所有函数参数都是传值传递,没有指针,并且所有的数据引用都是本地的自动栈变量,没有引用静态或全剧变量。

2.隐式可重入的:

调用线程小心的传递指向非共享数据的指针。

三、在线程化的程序中使用已存在的库函数

一句话,就是使用线程不安全函数的可重入版本,名字以_r为后缀结尾。

竞争

1.竞争发生的原因:

一个程序的正确性依赖于一个线程要在另一个线程到达y点之前到达它的控制流中的x点。也就是说,程序员假定线程会按照某种特殊的轨迹穿过执行状态空间,忘了一条准则规定:线程化的程序必须对任何可行的轨迹线都正确工作。

2.消除方法:

动态的为每个整数ID分配一个独立的块,并且传递给线程例程一个指向这个块的指针

死锁:

一组线程被阻塞了,等待一个永远也不会为真的条件。

Linux(12.1-12.6)学习笔记的更多相关文章

  1. linux内核分析第四周学习笔记

    linux内核分析第四周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...

  2. Linux内核分析第二周学习笔记

    linux内核分析第二周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...

  3. linux内核分析第一周学习笔记

    linux内核分析第一周学习笔记 标签(空格分隔): 20135328陈都 陈都 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.co ...

  4. Linux简易APR内存池学习笔记(带源码和实例)

    先给个内存池的实现代码,里面带有个应用小例子和画的流程图,方便了解运行原理,代码 GCC 编译可用.可以自己上网下APR源码,参考代码下载链接: http://pan.baidu.com/s/1hq6 ...

  5. Linux文件与目录管理(学习笔记)

    本笔记为<鸟哥linux私房菜>第六章学习笔记 一.目录与路径 相对路径与绝对路径 绝对路径:一定由根目录 / 写起              正确度比较好 相对路径:不是由 / 写起  ...

  6. Linux Shell输出颜色字符学习笔记(附Python脚本实现自动化定制生成)

    齿轮发出咔嚓一声,向前进了一格.而一旦向前迈进,齿轮就不能倒退了.这就是世界的规则. 0x01背景 造了个轮子:御剑师傅的ipintervalmerge的Python版本.觉得打印的提示信息如果是普通 ...

  7. linux云计算集群架构学习笔记:workstation 12.0 按装Red Hat Enterprise Linux 7(64位)

    安装RHEL7.2 步骤: 1.安装虚拟机,按以下截图安装即可  步骤2: Ret hat 7.2 操作系统安装 rhel7因为许可报错解决

  8. k8s 1.12 环境部署及学习笔记

    1.K8S概述 1.Kubernetes是什么 2.Kubernetes特性 3.Kubernetes集群架构与组件 4.Kubernetes核心概念 1.1 Kubernetes是什么 • Kube ...

  9. 《C++ Primer Plus》12.7 队列模拟 学习笔记

    Heather银行打算在Food Heap超市开设一个自动柜员机(ATM).Food Heap超市的管理者担心排队使用ATM的人流会干扰超市的交通,希望限制排队等待的人数.Heather银行希望对顾客 ...

随机推荐

  1. Resharper注册机实现以及源代码

    一直用Resharper,每次找注册机也麻烦,就想怎么才能自己能做个注册机,把它原理给搞懂了,不就不用找了.今天早上起来研究了下Resharper注册机,网上找到一位大神之前做Resharper注册机 ...

  2. nim3取石子游戏 (威佐夫博弈)

    http://www.cnblogs.com/jackge/archive/2013/04/22/3034968.html 有两堆石子,数量任意,可以不同.游戏开始由两个人轮流取石子.游戏规定,每次有 ...

  3. MySQL 如何修改字符集 utf8 改为 utf8mb4

    在实行sql server 向 mysql 迁移数据时,报错: Incorrect string value: '\xF0\x9F\x98\x8A' 原因是mysql 采用的是 utf8 的字符集,而 ...

  4. CentOS6.5(Python-2.7.12)安装Pip

    1.安装setuptools(下载链接可从https://pypi.python.org/pypi/setuptools#code-of-conduct寻找) #Download setuptools ...

  5. 转载的vim配置文件

    """"""""""""""""&quo ...

  6. Spring4定时器 cronTrigger和simpleTrigger实现方法

    spring4定时器 cronTrigger和simpleTrigger实现方法 Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制.Quartz 允许 ...

  7. redis unwatch discard

    UNWATCH UNWATCH 取消 WATCH 命令对所有 key 的监视. 如果在执行 WATCH 命令之后, EXEC 命令或 DISCARD 命令先被执行了的话,那么就不需要再执行UNWATC ...

  8. kafka basic commands

    kafka-server-start.sh config/server.properties & kafka-server-stop.sh kafka-topics.sh    --creat ...

  9. LESSON THREE

    安装redis,并使用ruby进行简单测试.             使用redis客户端ruby测试 安装gem yum install rubygems  安装ruby所需的redis-rb程序库 ...

  10. Unity3D 实现简单的语音聊天 [iOS版本]

    现在很多手机游戏中的聊天系统都加入语音聊天的功能,相比于传统的文字聊天,语音聊天在MMORPG中显得尤为重要,毕竟直接口头交流总比你码字快得多了,也更直观些. 实现语音聊天的方法很多,U3D中有不少第 ...