概念
进程
一个正在执行的程序,它是资源分配的最小单位
进程中的事情需要按照一定的顺序逐个进行
进程出现了很多弊端:
一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入轻型进程;
二是由于对称多处理机(SMP)出现,可以满足多个运行单位,而多个进程并行开销过大。(多个运行单位可以是多个进程也可以是多个线程)
线程
有时又称轻量级进程,程序执行的最小单位,系统独立调度和分派cpu的基本单位,它是进程中的一个实体。
一个进程中可以有多个线程,这些线程共享进程的所有资源,线程本身只包含一点必不可少的资源。
多线程的优势:
1、在多处理器中开发程序的并行性
2、在等待慢速IO操作时,程序可以执行其他操作,提高并发性
3、模块化的编程,能更清晰的表达程序中独立事件的关系,结构清晰
4、占用较少的系统资源
多线程不一定要多处理器
相关术语
并发(看起来同时发生)
并发是指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。
并行(真正的同时发生)
并行是指在同一时刻,有多条指令在多个处理器上同时执行。
同步
彼此有依赖关系的调用不应该“同时发生”,而同步就是要阻止那些“同时发生”的事情
异步
异步的概念和同步相对,任何两个彼此独立的操作是异步的,它表明事情独立的发生
线程的生命周期
主线程(初始线程)
1、当c程序运行时,首先运行main函数。在线程代码中,这个特殊的执行流被称作初始线程或者主线程。可以在主线程中做任何普通线程可以做的事情。
2、主线程的特殊性在于,它在main函数返回的时候,会导致进程结束,进程内所有的线程也将会结束。这不是一个好的现象,可以在主线程中调用pthread_exit函数,这样进程就会等待所有线程结束时才终止。
3、主线程接受参数的方式是通过argc和argv,而普通的线程只有一个参数void*
4、在绝大多数情况下,主线程在默认堆栈上运行,这个堆栈可以增长到足够的长度。而普通线程的堆栈是受限制的,一旦溢出就会产生错误。
创建线程
1、主线程是随着进程的创建而创建
2、其他线程可以通过调用函数来创建,主要调用pthread_create
线程 | 进程 | |
---|---|---|
标识符类型 | pthread_t | pid_t |
获取id | pthread_self() | getpid() |
创建 | pthread_create() | fork() |
pthread_t:
linux中:unsigned long int
Mac OS10.3、FreeBSD5.2中:结构体
int pthread_create(pthread_t *restrict tidp,
const pthread_attr_t *restrict attr,
void *(*start_routine)(void *),
void *restrict arg)
其中,
*restrict tidp:新线程的id,如果成功则新线程的id回填充到tidp指向的内存
*restrict attr:线程属性(调度策略,继承性,分离性...)
*(*start_routine)(void *):回调函数(新线程要执行的函数)
*restrict arg:回调函数的参数
返回值:成功返回0,失败则返回错误码
编译时需要连接库libpthread
注意:
新线程可能在当前线程从函数pthread_create返回之前就已经运行了,甚至新线程可能在当前线程从函数pthread_create返回之前就已经运行完毕了。
四个基本状态
就绪:线程能够运行,但是在等待可用的处理器
运行:当处理器选中一个就绪的线程执行时,它立刻变成运行状态。在多核系统中,可能同时有多个线程在运行
堵塞:线程在等待处理器以外的其他条件,如以下情况会造成堵塞
- 试图加锁一个已经被锁住的互斥量
- 等待某个条件变量
- 调用singwait等待尚未发生的信号
- 执行无法完成的I/O信号
- 由于内存页错误
终止:线程从启动函数中返回,或者调用pthread_exit函数,或者被取消
回收(线程的分离属性)
分离线程:当该线程结束时,回收其所属资源。
分离一个正在运行的线程并不会影响它,仅在线程结束后才会执行回收。
创建线程时默认是非分离的
- 一个没有被分离的线程在终止时会保留它的虚拟内存,包括他们的堆栈和其他系统资源,有时这种线程被称为“僵尸线程”。
- 如果线程具有分离属性,线程终止时会被立刻回收,回收将释放掉所有在线程终止时未释放的系统资源和进程资源,包括保存线程返回值的内存空间、堆栈、保存寄存器的内存空间等。
终止被分离的线程会释放所有的系统资源,但是必须释放由该线程占有的程序资源。由malloc或者mmap分配的内存可以在任何时候由任何线程释放,条件变量、互斥量、信号灯可以由任何线程销毁,只要他们被解锁了或者没有线程等待。但是只有互斥量的主人才能解锁它,所以在线程终止前,你需要解锁互斥量。
线程的基本控制
线程终止
普通的单个线程有一下3中方式退出,这样不会终止进程
- 从启动例程中返回,返回值是线程的退出码
- 线程可以被同一进程中的其他线程取消
- 线程调用pthread_exit(void *rval)函数,rval是退出码
return 和 pthread_exit 的区别:
主线程return时会结束进程,主线程pthread_exit时会等待所有线程结束后再结束进程
注意:
如果进程中的任意一个线程调用了exit,_Exit,_exit,那么整个进程就会终止
线程连接
int pthread_join(pthead_t tid, void **rval)
- 参数tid就是指定线程的id
- 参数rval是指定线程的返回码,如果线程被取消,那么rval被置为PTHREAD_CANCELED
- 该函数调用成功会返回0,失败返回错误码
调用该函数的线程会一直阻塞,直到指定的线程tid调用pthread_exit、从启动例程返回或者被取消
调用pthread_join会使指定的线程处于分离状态,如果指定线程已经处于分离状态,那么调用就会失败
线程分离
int pthread_detach(pthread_t thread);
pthread_detach可以分离一个线程,线程可以自己分离自己
成功返回0,失败返回错误码
线程取消
待续