第十二章 并发编程

第十二章 并发编程

概述:如果逻辑控制流在时间上重叠,那么他们就是并发的

应用级并发在其他情况下的应用

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

12.1基于进程的并发编程

构造并发程序最简单的方法就是用进程

常用函数

  • fork
  • exec
  • waitpid

原理

在父进程中接受客户端连接请求,然后创建一个新的子进程来为每个新客户端提供服务。

12.1.1基于进程的并发服务器

关于服务器需要说明的地方、

  • 要包括一个sigchld处理程序,来回收僵死程序
  • 父子进程必须关闭他们各自的connfd
  • 直到父子进程的connfd都关闭了,到客户端的链接才会终止

12.1.2关于进程的优劣

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

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

12.2基于I/O多路复用的并发编程

使用I/O多路复用的并发编程的原因

同时响应下面两个请求

  • 网络客户端发起连接请求
  • 用户在键盘上输入命令行

原理

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

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

事件驱动程序:将逻辑流模型化为状态机。
状态机:

  • 状态
  • 输入事件
  • 转移

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

1.优点

  • 相较基于进程的设计,给了程序员更多的对程序程序的控制
  • 运行在单一进程上下文中,所以每个逻辑流都可以访问该进程的全部地址空间,共享数据容易实现
  • 可以使用GDB调试
  • 高效
    2.缺点
  • 编码复杂
  • 不能充分利用多核处理器

12.3基于线程的并发编程

是以上两种方法的混合

  • 线程有内核自动调度
  • 多个线程运行在单一进程的上下文中

12.3.2posix线程

void *thread(void *vargp);
int main()
{
   pthread_t tid;
   Pthread_create(&tid, NULL, thread, NULL);
   Pthread_join(tid,NULL);
   exit(0);
}

void *thread(void *vargp)
{
   printf(“Hello , world\n”);
   return NULL;
}

12.3.3创建线程

线程通过调用pthread_create函数来创建其它线程

#include <pthread.h>
typedef void *(func)(void *);

int pthread_create(pthread_t *tid, pthread_attr_t *attr, func *f, void *arg);

12.3.4终止线程

  • 顶层的线程例程返回
  • 调用pthread_exit函数
  • 如果主线程调用,会先等待所有其他对等线程终止,再终止主线程和整个进程,返回值为pthread_return
    某个对等线程调用Unix的exit函数,会终止进程与其相关线程
  • 另一个对等线程通过以当前线程ID作为参数调用pthread_cancle来终止当前线程

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

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

12.5用信号量同步线程

第一个 CreateSemaphore

函数功能:创建信号量

函数原型:

HANDLE CreateSemaphore(

LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,

LONG lInitialCount,

LONG lMaximumCount,

LPCTSTR lpName

);

函数说明:

第一个参数表示安全控制,一般直接传入NULL。

第二个参数表示初始资源数量。

第三个参数表示最大并发数量。

第四个参数表示信号量的名称,传入NULL表示匿名信号量。

第二个 OpenSemaphore

函数功能:打开信号量

函数原型:

HANDLE OpenSemaphore(

DWORD dwDesiredAccess,

BOOL bInheritHandle,

LPCTSTR lpName

);

函数说明:

  • 第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。

  • 第二个参数表示信号量句柄继承性,一般传入TRUE即可。

  • 第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。

第三个 ReleaseSemaphore

函数功能:递增信号量的当前资源计数

函数原型:

BOOL ReleaseSemaphore(

HANDLE hSemaphore,

LONG lReleaseCount,

LPLONG lpPreviousCount

);

函数说明:

  • 第一个参数是信号量的句柄。

  • 第二个参数表示增加个数,必须大于0且不超过最大资源数量。

  • 第三个参数可以用来传出先前的资源计数,设为NULL表示不需要传出。

由于信号量是内核对象,因此使用CloseHandle()就可以完成清理与销毁了。

12.6使用线程提高并行性

四类线程不安全函数:

  • 不保护共享变量的函数

  • 保持跨越多个调用的状态的函数

  • 返回指向静态变量的指针的函数

  • 调用线程不安全函数的函数

可重入性:当函数被多个线程调用,不会引用任何共享数据

显式可重入:所有函数参数都是传值传递,所有数据引用都是本地自动栈变量

隐式可重入:显式可重入加上一些参数是引用传递(指向非共享数据的指针)

竞争:当一个程序的正确性依赖于,一个线程要在另一个线程到达y点之前到达它的控制流中的x点,就会发生竞争

线程化的程序必须对任何可行的轨迹线都正确工作

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

死锁不总是可以预测的,而错误常常不可重复。

学习心得

这周的内容和操作系统的内容有相似的地方,都是从分析并发执行的程序的状况。操作系统对于进程的调用,
进程的死锁情况的解决以及更多别的问题讲的更详细。但是本书更注重从代码的角度讲述并发编程的问题,所以通过
学习这门课,使我从不同的角度了解了操作系统。

参考文献:

  • 深入理解计算机系统第十二章
  • 20135202闫佳歆同学的博客
  • zhanghaodx082的专栏http://blog.csdn.net/zhanghaodx082/article/details/11932865
posted @ 2015-12-06 00:07  20135320赵瀚青  阅读(124)  评论(0编辑  收藏  举报