Concurrency Program(并发编程)

转载请注明出处:http://blog.csdn.net/c602273091/article/details/53538219

Introduction

之前学习网络编程的时候,采用的echo服务器是线性服务的。也就是说只能在一个时刻最多服务一个client。
这里写图片描述

那么服务器需要响应多个请求,那么就可以使用并发编程响应多个请求。

Different Concurrency Program

Process-based

基于进程的并发编程是比较简单的方式,就是服务器进行监听。然后listenfd接收到信号之后,就fork一个子进程。但是子进程会关闭它的监听描述符,父进程会关闭它的connfd以使它继续监听。如果不做这个的话,系统资源很快会被消耗殆尽。

很明显地,这种方法比较浪费资源,而且进程间通信很麻烦。因为进程是有自己的独立的内存地址空间的。需要使用IPC(这个东西不懂)~

如果要写一个多线程的服务器,那么用下面的代码就可以实现一个最简陋的版本。
这里写图片描述

当然上面的代码没有加上子进程回收,回收代码如下:
这里写图片描述

event-based

这种方法也叫做I/O多路复用技术。这种技术的思路就是使用select函数,要求内核挂起进程,在一个或者多个I/O发生后才将控制返回给运用程序。这种方法只有一个逻辑流,可以单步调试。但是代码写起来更加复杂,响应速度也相对比较慢,这种在实际使用中比较少使用。所以对于它的代码我就没有研究了。
这里写图片描述

thread-based

基于线程的并发编程集合了以上两种方法的优点,进程间可以共享代码、数据、堆、共享库、打开的文件。但是线程也有自己的ID、栈、栈指针、PC、通用寄存器和conditional code。而且线程和进程还有不同之处在于:线程是对等的,每个线程都有一样的权限,和创建先后顺序无关。

进程的模型如下:
这里写图片描述

加入线程后,可以表示为:
这里写图片描述

PS: 对于进程和线程,需要做一些比较。比如线程比进程更加高效,创建和回收都比进程快。同一个进程内,线程通信更快,更方便。

Posix Threads(PThreads)

Posix 线程是unix上处理C语言的一种标准接口。基本上所有的Unix上都可以支持Posix。

Posix的一些基本函数:
这里写图片描述

这里写图片描述
从这个Hello World你就可以看出如何创建、运行并回收一个thread。在这里需要注意的是如果传参有很多参数,使用结构体指针搞定。

话说回来,很多时候我们需要一些可以分离的线程来自生自灭,比如满足web服务器的要求,就可以使用pthread_detach。有的时候对于只执行一次的函数,我们可以使用pthread_once 进行初始化。

下面是基于多线程的服务器代码:
这里写图片描述
注意上面的Malloc,这是避免竞争的有效手段之一。

这里写图片描述

使用Thread一定要注意intended sharing。

Problem arise from Concurrency

在刚才谈到的多线程中,有一个不可忽视的存在就是共享数据。当多个线程对这些共享数据进行操作的时候,就会有竞争,那么就需要一些机制保证这些共享数据的安全。

当然,我们首先需要知道的是什么是共享变量。
当一个变量被一个以上的线程引用的时候,那么该变量就是共享的。在这种定义下,除了全局变量和static属性的本地变量之外,本地自动变量也有可能成为共享变量。如下图的使用:
这里写图片描述

那么如何解决共享变量带来的同步错误呢?在这里我们首先需要分析由共享变量带来的临界区(critical section)。在临界区,访问应该是互斥的。有两个临界区的交集就是不安全区,在不安全区的轨迹就是不安全的轨迹线(unsafe trajectory)(这里描绘的轨迹线都是进度图,process graph)为了使得进度图的轨迹是安全轨迹,就需要使用一些同步的技术。在这个方面,Dijkstra提出的信号量(Semaphore)就是其中一种。当然还有Pthread的Mutex、JAVA的monitors。在这里主要是讲信号量这一种经典方法。

信号量是一种非负的变量,能够使用P、V操作。
这里写图片描述

信号量相关函数:
这里写图片描述

对于之前有同步错误的cnt的计算进行重新加互斥锁达到保护共享变量的目的。
这里写图片描述

More Problems

在利用信号量调度共享资源的时候,有两个经典的问题还是需要进行了解。生产者-消费者问题,读者-写者问题。

生存者-消费者问题:
这里写图片描述
这里写图片描述
这个模型是二元信号量。生产一个,消费一个,按照一定的顺序进行。所以这是线程安全的。

这里写图片描述
对于这个问题,写了一个sbuf进行同步。
这里写图片描述

对sbuf进行初始化,使用了三个信号量。一个用于保护buf,另外两个使用来记录生产-消费的数目。使得生产和消费的总和一直n。
这里写图片描述

这里写图片描述

这里写图片描述

通过对以上的sbuf的生产-消费模型进行分析,可以发现其实这个和刚才只有一个生产者和一个消费者的时候其实是一样的。都是为了使得生存者不会生产太多超出容量,使得消费者不会消费小于0的东西。我觉得在proxy lab的cache用这个做同步真是好极了。

读者-写者问题:
写是一种互斥的访问,读是无限制的。然后读写顺序,读优先,写优先都会导致饥饿问题。
对于写优先,代码如下:
这里写图片描述

TIPS:
Races:结果不确定,依赖于某些特定的执行顺序。
会有race的情况:
这里写图片描述

解决race,使用malloc。
这里写图片描述

Deadlock:A需要B释放的资源,B也在等待A释放资源。
这里写图片描述
这是很典型的死锁,如果两个线程一起执行。然后两个线程都会挂在那里,因为经过P操作以后,s0,s1都是0了,不能进行接下来的P操作了。
对它进行修改如下:
这里写图片描述
上图代码红色部分应该是错的。P应该是P(&mutex[1-id]); P(&mutex[id]);

Starvation:某些程序调度,使得某些程序永远得不到执行。

线程安全:
多个线程调用,执行顺序都是正确的结果。
一共有四种不安全线程:
这里写图片描述

解决办法就是:
第一种就是用信号量;第二类重写;第三类:加锁拷贝;第四类:使用线程安全的函数。
这里写图片描述

PS:以上PPT的截图来自于CMU 15-213课程网站请戳这里

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值