一、线程基础
1、线程一般有两种,内核线程和用户线程。内核线程(轻量级进程):运行在内核,由内核来调度;用户线程:运行在用户空间,由线程库来调度,当它获得CPU的使用权时,它就加载并运行。
2、内核的最小调度单位是进程,CPU最小的调度单位是线程。
3、关于线程的API
**********创建线程**********
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg)
thread:新线程的标识符。
attr:新线程的属性,NULL表示默认。
start_routine:新线程将运行的函数。
arg:其运行函数的参数。
成功返回0,失败返回错误码。
***********退出线程***********
void pthread_exit(void* retval)
通过retval参数向线程的回收者传递其退出信息,且永远不会失败。
***********回收其他线程************
int pthread_join(pthread_t thread,void **retval)
retval:目标线程返回的退出信息
该函数会一直阻塞,直到被回收的线程结束为止。成功返回0,失败则返回错误码。
EDEADLK:引起死锁,比如两个线程互相针对对方调用pthread_join,或者线程对自身调用pthread_join。
***********取消线程/终止线程***********
int pthread_cancel(pthread_t thread)
成功返回0,失败返回错误码。
***********将线程设置为脱离线程**********
脱离线程:脱离了与其他线程的同步的线程,脱离线程在退出时将自行释放其占用的系统资源。
设置方法:设置线程属性,或者调用pthread_detach函数
4、线程的属性:
(1)、线程的脱离状态。
(2)、线程堆栈的起始地址和大小。
(3)、保护区域大小。
(4)、线程调度策略:轮转算法、先进先出算法。
(5)、优先级的有效范围。
二、信号量
1、原理与进程信号量相同
2、函数
************初始化一个未命名的信号量************
int sem_init(sem_t* sem,int pshared,unsigned int value);
pshared:信号量的类型,如果为0,则为当前进程局部信号量,否则,可在多进程间共享
value:信号量的初始值
************销毁信号量*************************
int sem_destory(sem_t* sem)
************将信号量的减1(阻塞版)****************
int sem_wait(sem_t* sem)
************将信号量的值减1(非阻塞版)**************
int sem_trywait(sem_t *sem)
************将信号量的值加1**********************
inr sem_post(sem_t *sem)
///
所有函数成功返回0,失败返回-1
三、互斥量/互斥锁
1、原理:当进入关键代码段时,进行加锁,离开关键代码段的时候,解锁。
2、API
************初始化互斥锁************
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t* mutexattr);
mutex:指向遥操作的目标互斥锁
mutexattr:互斥锁属性
************销毁互斥锁*************************
int pthread_mutex_destory(pthread_mutex_t *mutex)
************加锁(阻塞版)****************
int pthread_mutex_lock(pthread_mutex_t *mutex)
************加锁(非阻塞版)**************
int pthread_mutex_trylock(pthread_mutex_t *mutex)
************解锁*********************
inr pthread_mutex_unlock(pthread_mutex_t* mutex)
///
所有函数成功返回0,失败返回-1
3、互斥量的属性:pshared:是否允许跨进程共享;type:类型:普通锁,检错锁,嵌套锁,默认锁。
4、死锁产生的原因:在一个线程中,对一个已经加锁的普通锁再次加锁。或者说,多线程为了等待竞争资源而形成的僵局。
5、死锁产生的必要条件
产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
(1)、互斥条件:在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
(2)、不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走
(3)、请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
(4)、循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。即存在一个处于等待状态的进程集合{Pl, P2, ..., pn},其中Pi等 待的资源被P(i+1)占有(i=0, 1, ..., n-1),Pn等待的资源被P0占有。
6、应对死锁的策略:不理睬,打破必要条件
7、避免死锁的策略:银行家算法
四、条件变量
1、原理:当某个共享数据达到某个值的时候,唤醒等待这个共享数据的线程。
2、函数
************初始化一个未命名的信号量************
int sem_init(sem_t* sem,int pshared,unsigned int value);
pshared:信号量的类型,如果为0,则为当前进程局部信号量,否则,可在多进程间共享
value:信号量的初始值
************销毁信号量*************************
int sem_destory(sem_t* sem)
************将信号量的减1(阻塞版)****************
int sem_wait(sem_t* sem)
************将信号量的值减1(非阻塞版)**************
int sem_trywait(sem_t *sem)
************将信号量的值加1**********************
inr sem_post(sem_t *sem)
///
所有函数成功返回0,失败返回-1
三、互斥量/互斥锁
1、原理:当进入关键代码段时,进行加锁,离开关键代码段的时候,解锁。
2、API
************初始化条件变量************
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t* cond_attr);
cond:指向要操作的目标条件变量
cond_attr:条件变量的属性
************销毁条件变量*************************
int pthread_cond _destory(pthread_cond_t *cond)
************唤醒所有等待目标条件变量的线程****************
int pthread_cond _broadcast(pthread_cond_t *cond)
************唤醒一个等待目标条件变量的线程**************
int pthread_cond _signal(pthread_cond_t *cond)
************等待目标条件变量*********************
inr pthread_cond _wait(pthread_cond_t *cond,pthread_mutex_t *mutex)
mutex:用于保护条件变量的互斥锁
///
所有函数成功返回0,失败返回-1
四、可重入函数:
1、如果一个函数能够被多个线程同时调用而且不发生竞态条件,则我们称它是线程安全的,或者说它是可重入函数。
2、如果一个多线程程序的某个线程调用了fork函数,则新创建的子进程不会自动创建与父进程相同数量的线程,子进程只有一个线程,它是调用fork的那个线程的完整复制。
3、子进程会继承父进程的锁的状态。因为子进程无法知道锁的状态,如果在进行加锁,则很有可能造成死锁,pthread_atfork函数可以避免这个问题,它先锁住全部的锁,在fork之后,解除父进程的锁,
参考资料:《Linux高性能服务器编程》