线程管理
线程介绍
线程就是进程中负责执行的部分,是进程内部的控制序列,它是轻量级的,没有自己独立的代码段(txt)、数据段(静态数据bss、全局数据段data)、堆区(heap)、环境变量、命令行参数、文件描述符、信号处理函数、当前工作目录。
线程拥有独立的栈内存,也就是它自己独立的局部变量。
一个进程中至少有一个线程,我们把它叫作主线程,也可以再创建多个线程。
注意:进程是个资源单位,而线程是个执行单例,线程是进程的一部分,进程中正是有了线程才能动起来。
线程和进程的区别(重要):
进程是资源单位,线程是执行单例,线程是进程的一部分,他们之前不具备可比性。
关系:线程是进程的基本执行单元,一个进程的所有任务都在线程中执行;进程要想执行任务,必须得有线程。
区别:1、同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间;2、同一进程内的线程共享本进程的资源,而进程间的资源是独立的。
POSIX线程
1、早期各计算机厂商自己提供私有的线程库,接口实现的差异非常大,不利于开发和移植。
2、世界标准化组织于1995年,制定了统一的线程接口标准,遵循该标准的线程被统称为POSIX线程,简称pthread。
1、pthread包含一个头文件pthread.h和一个共享库libpthread.so。
线程函数
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能:创建一个线程
thread:输出型参数,用于返回新创建线程的线程ID
attr: 线程属性,一般写NULL即可。
start_routine:线程的入口函数,相当于主线程的main函数
arg:传递给线程入口函数的参数(想传多个给结构体)
功能:成功返回0,失败返回-1。
使用例子:
void * run(void *arg)
{
.....
}
pthread_t tid;
pthread_create(&tid,NULL,run,NULL)
注意:从表面来看当主线程结束后,子线程会跟着一起结束,但实事情况并不是这样,子线程之所以结束是因为主线程执行了main函数中隐藏的return语句,导致整个进程退出,所以子线程才结束,如果主线程调用pthread_exit自杀,这样就没线程执行隐藏的return语句了,进程就不会结束,子线程也不会结束。
int pthread_join(pthread_t thread, void **retval);
功能:等待指定的线程结束,回收其资源,获取返回值。
thread:等待线程的ID
retval:用于存储存储线程的返回值
pthread_t pthread_self(void);
功能:获取当前线程的ID
int pthread_equal(pthread_t t1, pthread_t t2);
功能:判断两个线程ID是否相同
返回值:相同返回真,不同返回假
void pthread_exit(void *retval);
功能:结束当前线程
retval:返回给pthread_join函数,作为线程结束时的返回值
int pthread_cancel(pthread_t thread);
功能:让指定的线程结束
int pthread_setcancelstate(int state, int *oldstate);
功能:设置当前线程是否能被取消
state:
PTHREAD_CANCEL_ENABLE 允许取消
PTHREAD_CANCEL_DISABLE 禁止取消
oldstate:
获取线程旧的取消状态
int pthread_setcanceltype(int type, int *oldtype);
功能:设置线程的取消类型
type:
PTHREAD_CANCEL_DEFERRED 在合适的时间取消
PTHREAD_CANCEL_ASYNCHRONOUS 立即取消
oldtype:
获取线程旧的取消类型
int pthread_detach(pthread_t thread);
功能:与指定的子线程分离,让它自己回收资源
thread:子线程的线程ID
主线程与子线程传参
1、创建线程时传参
把传给子线程的数据的内存首地址写在pthrread_create函数的最后一个参数。
是否要进行传与数据存在那块内存无关,与数据的作用域有关。
2、线程的返回值
返回一个地址,该地址指向一块内存,里面存储着要返回给主线程的数据。
但该内存不可以是线程的栈内存,因为线程一但结束它的栈内存会被释放。
线程的结束
1、从线程入口函数return
2、调用pthread_exit
注意:如果线程调用exit函数,将结束整个进程。
线程的取消操作
一个线程让另一个线程结束,但也看接收着是否同取消。
线程分离
主线程创建子线程后,它可以等待子线程结束(回收资源)。
当主线程与子线程分离后,子线程可以自己回收资源
// 销毁线程属性结构体 // 初始化线程属性结构体
pthread_attr_destroy pthread_attr_init
// 获取线程栈内存警戒区大小 // 设置线程栈内存警戒区大小
pthread_attr_getguardsize pthread_attr_setguardsize
// 获取线程的调度策略的来源 // 设置线程的调度策略的来源
pthread_attr_getinheritsched pthread_attr_setinheritsched
PTHREAD_INHERIT_SCHED 默认继承创建者的高度策略
PTHREAD_EXPLICIT_SCHED 根据参数决定(schedpolicy、schedparam才有意义)
// 获取线程的调度策略 // 设置线程的调度策略
pthread_attr_getschedpolicy pthread_attr_setschedpolicy
SCHED_FIFO, 先进先出策略
SCHED_RR 轮转策略
SCHED_OTHER 普通策略(默认,优先级,schedparam才有意义)
// 获取线程的优先级 // 获取线程的优先级
pthread_attr_getschedparam pthread_attr_setschedparam
// 获取线程的竞争范围 // 设置线程的竞争范围
pthread_attr_getscope pthread_attr_setscope
PTHREAD_SCOPE_SYSTEM:在系统内存竞争
PTHREAD_SCOPE_PROCESS:进程内竞争,但Linux系统不支持该选项
// 获取栈内存的起始地址和大小 // 设置栈内存的起始地址和大小
pthread_attr_getstack pthread_attr_setstack
// 获取栈内存的起始地址 // 设置栈内存的起始地址
pthread_attr_getstackaddr pthread_attr_setstackaddr
// 获取栈内存的大小 // 设置栈内存的大小
pthread_attr_getstacksize pthread_attr_setstacksize
线程同步
当多个线程同时访问一个资源时,如果不进行协调可能会产生数据混乱、操作失效等现在,我们需要使用一些特殊的技术保障线程之间协同工作,这些技术叫线程同步技术。
互斥量
pthread_mutex_t 也叫互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
功能:初始化互斥量
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:给互斥量加锁,如果该互斥量已经被加锁,该函数会被阻塞
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:尝试给互斥量加锁,如果不能加则立即返回
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:给互斥量解锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:销毁互斥量
条件变量
pthread_cond_t 能够让线程进入要睡眠的对象,当一个线程达到特定的条件后可以睡入条件变量,然后在某个时刻被其它线程叫醒。
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
功能:初始化条件变量
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
功能:让当前线程睡入cond条件变量,并自动解锁。
int pthread_cond_signal(pthread_cond_t *cond);
功能:叫醒条件变量中的一个线程,线程会从pthread_cond_wait返回,并对互斥常量加锁,如果无法成功加锁则继续阻塞。
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:叫醒条件变量中的所有线程,条件变量中的线程都会从pthread_cond_wait返回,并对互斥常量加锁,如果无法成功加锁则继续阻塞。
int pthread_cond_timedwait(pthread_cond_t*cond, pthread_mutex_t* mutex, const struct timespec *abstime);
功能:睡入条件变量,并自动解锁,并设置睡眠时间。
int pthread_cond_destroy(pthread_cond_t *cond);
功能:销毁条件变量
死锁问题
死锁的定义
多个进程或线程互相等待对方我资源,在得到对象的资源之前,都不会释放自己的资源,这种造成循环等待的现象叫死锁。
产生死锁的四大必要条件
a、资源互斥不能共享
b、占用且等待
c、资源不可剥夺
d、环路等待
防止产生死锁方法
1、破坏互斥条件
想办法让资源能够共享,解决方法就是增加资源,缺点:如果资源是硬件,可能会有经济上的浪费。
2、破坏请求并保持条件
在程序运行前一次分配所有它所需要的资源,否则不执行,缺点:可能会浪费系统资源,因为有些资源可能短时间用不上。
3、破坏不可剥夺的条件
当请求新的资源失败时释放旧的资源,过一段时间后再重新获取所有资源,缺点反复的释放申请资源会浪费时间。
4、破坏循环等待的条件
把所有的资源进行编号,所有线程按照顺序进行请求资源,缺点:编号必需要要稳定,这样就限制的新的资源加入。
死锁的检测
1、画出资源的分配图
2、简化资源分配图
3、按照产生死锁的四大条件来检查是有请求环路