进程是系统中程序执行和资源分配的最小单位。每个进程都拥有自己的数据段,代码段和堆栈段。这就造成了进程在进行切换等操作时需要有比较负责的上下文切换等动作。为了进一步减少处理机的空转时间支持多处理器和减少上下文开销,由此有线程的提出。
线程是进程内的一种基本调度单位,也可以称作轻量级进程。线程是在共享内存空间中并发的多道执行路径,它们共享一个进程的资源,如文件描述符,信号处理。从而大大减少了上下文切换的花销。
同进程一样,线程也将相关的变量值放在线程控制表内,一个进程有多个线程,但却共享一个用户地址空间,这样同步的问题显得非常重要。
线程按照调度者可以分为用户级线程和核心级线程。
用户级线程 调度算法和调度过程有用户自行决定,缺点在于,如果一个进程中的某一个线程调用一个阻塞的系统调用,则该进程中的其他线程也会被阻塞。这样就在一个进程中的多个线程的调度无法发挥出多处理器的优势。
核心级线程 这个线程允许不同进程中的线程按照同一个相对优先调度方法进行调度,这样就能发挥出多处理器的并发优势。
二.Linux下线程实现这边说到的线程操作都是用户空间线程的操作。
1. Pthread_create,执行线程函数,结束之后自动退出
Pthread_exit 是主动退出线程,在调用退出函数时,不能使用exit
因为exit函数是使得调用进程终止,而一个进程中往往包含有多个线程,这会导致该进程中的所有线程终止。所以必须使用pthread_exit退出对应线程。
#include<pthread.h>
Pthread_join等待线程结束
例如,
Void thread1(){}
Pthread_t id1;
Pthread_create(id1,NULL,(void *)thread1,NULL);
Pthread_join(id1,NULL);
Thread1为线程执行函数,create中第一个NULL为线程属性,第二个NULL为执行函数的形参。
2. 修改线程属性
线程属性主要包括绑定属性,分离属性,堆栈地址,优先级,其中系统默认的属性为非绑定,非分离,缺省1M的堆栈,与父进程同样级别的优先级。
绑定属性 一个用户线程对应一个内核线程,非绑定情况下不是对应固定的内核线程,而是由系统分配。
分离属性 在非分离(attach)状态下,线程结束时不释放线程资源,只有调用pthread_join之后才会释放线程资源,而设置了分离属性(detach)之后,会在线程结束之后自动释放系统资源。在设置分离属性之后,如果线程函数执行的很快释放线程资源之后,pthead_create会返回错误值。所以一般设置为非分离属性。
Pthread_attr_init 初始化属性
Pthread_attr_setdetachstate 设置线程分离属性
Pthread_attr_setscope 设置线程绑定属性
Pthread_attr_getschedparam 获取线程优先级
Pthread_attr_setschedparm 设置线程优先级
3. 线程访问控制 mutex互斥锁控制
pthread_mutex_init
pthread_mutex_lock
pthread_mutex_unlock
pthread_mutex_trylock 互斥锁上锁
pthread_mutex_destroy 消除互斥锁
用户线程的互斥锁包括快速互斥锁,递归互斥锁和检错互斥锁。
快速互斥锁:线程会阻塞直至拥有互斥锁的线程解锁为止,PTHREAD_MUTEX_INITIALIZER
递归互斥锁:成功返回并且增加线程在互斥上加锁的次数
检错互斥锁:快速互斥锁的非阻塞版本
Pthread_muxtex_t mutex=PTHREAD_MUTEX_INITIALIZER
Pthread_mutex_init(&mutex,NULL);
或者 mutex_t mutex;
Pthread_mutex_init(&mutex,PTHREAD_MUTEX_INITIALIZER);
4. 线程控制访问 信号量 互斥和同步
信号量为0 时无权访问,所以如果sem=1,则会互斥锁
如果有两个信号,sem1=1,sem2=0,则为同步所用。
例子如下,
如果一个信号量比1大,就是可以被多个线程调用,直至信号值为0
而同步操作要求信号量得数目至少为2个
函数使用:
Sem_init sem_init(&sem,0,1)互斥,中间的0表示不支持信号量共享。
Sem_wait和Sem_trywait 为减操作(P),如果小于等于0时,sem_wait会阻塞进程,而sem_trywait会立即返回。
Sem_post 为加操作(V),同时发出信号唤醒等待的进程。
Sem_getvalue 得到信号量的值
Sem_destroy 删除信号量