并发编程学习记录
重要知识点
现代系统基本的构造并发程序的方法
- 进程:用这个方法,每个逻辑流都是一个进程,由内核来调度和维护。因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用某种显式的进程间通信机制。
- I/O多路复用:在这种形式的并发编程中,应用程序在一个进程的上下文中显示地调度它们自己的逻辑流。逻辑流被模型化为状态机,数据到达文件描述符后,主程序显示地从一个状态转化到另一个状态。因为程序是一个单独的进程,所以所用流共享同一个地址空间。
- 线程:线程是运行在一个单一进程上下文中的逻辑流,由内核进行调度。由内核进行调度,共享同一个虚拟地址空间。
基于进程的并发编程的优缺点
- 优点:进程不可能不小心覆盖另一个进程的虚拟存储器。
- 缺点:独立的地址空间使得进程共享状态信息变得更加困难。为了共享信息,它们必须使用显示的IPC(进程间通信)机制,而进程控制和IPC的开销很高,因此会比较慢。
基于I/O多路复用技术的并发编程的优缺点
-
优点:
-
它比基于进程的设计给了程序员更多的对程序行为的控制。
-
每个逻辑流都能访问该进程的全部地址空间,这使得流之间共享数据变得容易。
-
-
缺点:编码复杂。
线程与线程池
线程不是按照严格的父子层次来组织的。和一个线程相关的线程组成一个对等(线程)池独立于其他线程创建的线程。
主线程和其他线程的区别仅在于它总是进程中第一个运行的线程。在对等线程池中,一个线程可以杀死它的任何对等线程,或者等待它的任意对等线程终止。另外,每个对等线程都能读写相同的共享数据。
Posix线程
Posix线程是在C程序中处理线程的一个标准接口。
-
创建线程
线程通过调用pthread_create函数来创建其他进程。
Pthread_create函数创建一个新进程,带着一个输入变量arg,在线程的上下文中运行线程例程f。能用attr参数来改变新创建线程的默认属性。当pthread_create返回时,参数tid包含新创建线程的ID。新线程可以通过调用pthread_self函数来获得它自己的线程ID。
-
终止线程
一个线程可以通过以下方式终止:
-
当顶层的线程例程返回时,线程会隐式地终止。
-
通过调用pthread_exit函数,线程会显示地终止。如果主线程调用pthread_exit,它会等待所有其他对等线程终止,然后再终止主线程和整个进程,返回值为thread_return。
-
某个对等线程调用Unix的exit函数,该函数终止进程以及所有与该进程有关的线程。
-
另一个对等进程通过以当前线程ID作为参数调用pthread_cancle函数来终止当前线程。
-
-
回收已终止线程的资源
线程通过调用pthread_join函数等待其他线程终止
pthread_join函数会阻塞,直到线程tid终止,将线程例程返回的(void*)指针赋值为thread_return指向的位置,然后回收已终止线程占用的所有存储器资源。
-
分离线程
线程是可结合或可分离的。
-
一个可结合的线程能够被其他线程回收其资源和杀死,在被其他线程回收之前,它的存储器资源是没有被释放的。
-
一个分离的线程是不能被其他线程回收或杀死的。它的存储器资源在它终止时由系统自动释放。
-
线程存储器模型
一组并发线程运行在一个进程的上下文中。每个线程都有它自己独立的线程上下文,包括线程ID、栈、栈指针、程序计数器、条件码和通用目的寄存器值。每个线程和其他线程一起共享进程上下文的剩余部分。这包括这个用户虚拟地址空间,它是由只读文本(代码)、读/写数据、堆以及所有的共享库代码和数据区域组成的。线程也共享同样的打开文件集合。
变量映射到存储器
线程化的C程序中变量根据它们的存储类型被映射到虚拟存储器中。
-
全局变量:全局变量是定义在函数之外的变量。在运行时,虚拟存储器的读/写区域只包含每个全局变量的一个实例,任何线程都可以引用。
-
本地自动变量:本地自动变量就是定义在函数内部但是没有static属性的变量。在运行时,每个线程的栈都包括它自己的所有本地自动变量的实例。即使当多个线程同一个线程例程也是如此。
-
本地静态变量:是本地静态变量是定义在函数内部并有static属性的变量。和全局变量一样,虚拟存储器的读/写只包含在程序中声明的每个本地静态变量的一个实例。
线程安全
一个函数被称为线程安全的,当且仅当被多个并发线程反复调用时,它会一直产生正确的结果。
线程不安全函数类:
3.返回指向静态变量的指针的函数。
4.调用线程不安全的函数。
可重入函数
一类重要的线程安全函数,其特点在于他们具有这样一种属性:当它们被多个线程调用时,不会引用任何共享数据。
参考资料
《深入理解计算机系统》第12章并发编程。