线程是执行的基本单位,每个线程都有自己的tid,有自己的TCB,如果说进程中只有一个线程,那么这个线程就是进程的主线程,线程共享进程的资源,也有自己私有的资源
进程和线程区别:
共性:都为操作系统提供了并发执行能力
不同点:
调度和资源:线程是系统调度的最小单位,进程是资源分配的最小单位
地址空间方面:同一个进程创建的多个线程共享进程的资源;进程的地址空间相互独立
通信方面:线程通信相对简单,只需要通过全局变量可以实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制(借助3g-4g内核空间)
安全性方面:线程安全性差一些,当进程结束时会导致所有线程退出;进程相对安全
那么什么是私有资源,多任务可以使用多进程实现,为什么要引入多线程呢?
两个进程交流通讯需要调用系统内核,两线程之间则不需要,但是线程中的临界资源当两线程同时访问是会互相打扰,因为线程是异步的,因此要锁住线程,或者让他们称为同步的原子操作
#include<pthread.h>
int pthread_create(pthread_t*thread,const pthread_attr_t*arr,void*(*srart_route)(void*),void*arg);
功能:创建一个新线程
参数:
thread:将新线程的id存储到这个地址空间
attr:NULL,使用缺省属性
arg:传递给线程函数的唯一参数
start_route:函数指针指向函数的入口地址(线程函数)
返回值:
成功返回0
失败返回非零数字,errno被设置
注:编译时要链接-pthread库
pthread_t pthread_self(void);
功能:获取当前线程的ID
返回值:返回当前线程的tid(unsigned long类型)
线程的终止、汇合和分离
①终止
pthread_exit(void*retval);
功能:终止当前县城
参数:
retval:通过此参数,将线程的退出状态码传递给进程中调用
无返回值
②进程的汇合和分离
int phread_detach(pthread_t thread);
功能:分离一个线程
参数:线程id
返回值:成功返回0,失败返回非零,errno被设置
int pthread_join(pthread_t thread,void**retval);
功能:汇合一个已终止的线程(阻塞等待)
参数:
thread:指定了要汇合的线程id
**retval:将可汇合的线程的退出状态码拷贝到*retval中,如果是取消的线程,将PTHREAD_RANCELED放到*retval中
③线程的取消
int pthread_cancel(pthread_t thread);
功能:发送一个取消请求给指定的线程
参数:
thread:指定要取消的线程id
返回值:成功返回0,失败返回非零,errno被设置
可重入函数:只能访问自己的栈帧内容,每一次调用都是独立栈帧
不可重入函数:如果函数访问了自己栈帧以外的空间,例如访问了malloc,全局变量,静态局部变量等
代码演示:
线程同步
mutex锁、条件变量和POSIX信号量
线程访问临界资源时会互相打扰,这些工具就是为了一次只有一个线程来访问临界资源,一个线程访问完成后,下一个线程再来访问
看一段代码:
所以我们要在访问共享资源的地方给它编程原子操作,必须把它作为整体执行才可以
mutex锁:是一个互斥设备,用于同时修改共享数据时对共享数据的保护
①加锁成功,访问数据,解锁
②加锁失败,等待(或者不等待返回错误)
两种状态:locked和unlocked
创建一个mutex锁:pthread_mutex_t
用mutex锁等于PTHREAD_MUTEX_INITIALIZER也可以实现初始化
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t*restrict attr);
功能:使用缺省属性初始化mutex
参数:
mutex:指定要初始化的mutex
attr:NULL,使用缺省属性初始化mutex锁
返回值:成功总是0;失败会返回errno
int pthread_mutex_destory(pthread_mutex_t*mutex);
功能:当处于解锁状态下,销毁mutex锁
参数:mutex指定被销毁的mutex锁
返回值同上
int pthread_mutex_lock(pthread_mutex_t*mutex);
功能:如果mutex锁目前是unlocked状态,当前进程锁定此锁,立即返回
如果mutex锁已被其他线程锁定,当前线程挂起直到锁被解除
参数:指定要操作的mutex锁
返回值同上
int pthread_mutex_trylock(pthread_mutex_t *mutex);
功能:与lock一样,只是如果已经锁定,立即返回,并返回EBUSY
返回值同上
int pthread_mutex_unlock(pthread_mutex_t*mutex);
功能:解锁,此锁一定是被当前线程占有并锁定的
参数:mutex指要解除的锁
返回值同上
用mutex锁修改上述代码
条件变量(pthread_cond_t类型)
同步设备,允许多线程挂起执行直到条件被满足
#include<pthread.h>
int pthread_cond_init(pthread_cond_t*cond,pthread_condattr_t*cond_attr);
功能:使用缺省属性初始化条件变量
参数:
cond:要初始化的条件变量
cond_attr:NULL缺省属性
返回值:
成功返回0
失败返回非零,errno被设置
让条件变量等于PTHREAD_COND_INITIALIZER这个宏也可以初始化
int pthread_cond_destory(pthread_cond_t*cond);
功能:销毁条件变量,无线程等待的情况
参数:cond要销毁的条件变量
int pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t*mutex);
功能:
①原子的解锁等待条件变为真
②mutex必须在pthread_cond_wait调用前处于locked状态
③函数返回前,必须重新锁定mutex锁
参数:
cond:指定要操作的条件变量
mutex:使用到的mutex锁
int pthread_cond_timedwait(pthread_cond_t*cond,pthread_mutex_t*mutex,const struct timespec*abstime);
功能:和wait相同,只是指定了一个等待周期,如果过期条件变量还没变为真,重新获取mutex锁,返回ETIMEOUT
int pthread_cond_signal(pthread_cond_t*cond);
功能:从因此条件变量阻塞的线程中取出一个解除阻塞,如果没有则什么也不发生
参数:
cond:指定条件变量
int pthread_cond_broadcast(pthread_cond_t*cond);
功能:所有因此条件变量阻塞的线程都会被解除阻塞,如无线程等待条件为真,则什么也不做
参数:cond指定具体的条件变量
用代码来看一下:
POSIX 信号量(>=0)整数
可以将临界资源在分割成多个区域,这样多个执行流需访问临界资源时,可以让这些执行流同时访问临界资源的不同区域,此时就不会出现数据不一致的问题,信号量本质也是一种临界资源
申请信号量(P)-1操作
释放信号量(V)+1操作
信号量的PV操作必须是原子操作
#include<semaphore.h>
int sem_init(sem_t*sem,int pshared,unsigned int value);
功能:初始化一个匿名信号量地址
参数:
sem:要初始化的信号量
pshared:传入0表示线程间共享,非零表示进程间共享
value:信号量的初始值
返回值:
成功返回0
失败返回-1,errno被设置
int sem_destroy(sem_t*sem);
功能:销毁一个匿名信号量
参数:
sem:指定要销毁的匿名信号量地址
返回值:同上
int sem_post(sem_t*sem);//V操作,信号量值+1操作,信号量为0将阻塞,大于0时唤醒进程
int sem_wait(sem_t*sem);//P操作,信号量-1操作,信号量为0将阻塞,大于0时唤醒进程
int sem_trywait(sem_t*sem);//尝试P操作
int sem_timedwait(sem_t*sem,const struct timespec*abs_timeout);//超时等待
用代码来看一下