一、线程的取消
int pthread_cancel(pthread_t thread);
注意:
线程的取消要有
取消点
才可以,不是说取消就取消,线程的取消点主要是阻塞的系统调用
- 方法一:通过
延时
让线程阻塞 - 方法二:使用设置取消点函数
void pthread_testcancel(void)
;
设置取消线程的其他函数
(1)设置取消使能或禁止
int pthread_setcancelstate(int state, int *oldstate);
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
(2)设置取消类型
int pthread_setcanceltype(int type, int *oldtype);
PTHREAD_CANCEL_DEFERRED 等到取消点才取消
PTHREAD_CANCEL_ASYNCHRONOUS 目标线程会立即取消
运行段错误调试:
可以使用gdb调试
使用gdb 运行代码,gdb ./youapp
(gdb) run
等待出现Thread 1 "pcancel" received signal SIGSEGV,
Segmentation fault.
输入命令bt(打印调用栈)
(gdb) bt
#0 0x00007ffff783ecd0 in vfprintf () from /lib/x86_64-linux- gnu/libc.so.6
#1 0x00007ffff78458a9 in printf () from /lib/x86_64-linux-gnu/libc.so.6
#2 0x00000000004007f9 in main () at pcancel.c:21
确定段错误位置是pcancel.c 21行
二、线程的清理
当线程
非正常终止
,需要清理线程所占的空间。
void pthread_cleanup_push(void (*routine) (void *), void *arg)
void pthread_cleanup_pop(int execute)
routine 函数被执行的条件:
- 被
pthread_cancel
取消掉。 - 执行
pthread_exit
非0
参数执行pthread_cleanup_pop()
注意:
- 必须
成对使用
,即使pthread_cleanup_pop不会被执行到也必须写上,否则编译错误
。 pthread_cleanup_pop()
被执行且参数为0
,pthread_cleanup_push
回调函数routine
不会被执行.pthread_cleanup_push
和pthread_cleanup_pop
可以写多对,routine
执行顺序正好相反- 线程内的
return
可以结束线程,也可以给pthread_join返回值
,但不能触发
pthread_cleanup_push里面的回调函数
,所以我们结束线程
尽量使用pthread_exit
退出线程。
三、线程的互斥和同步
必要性: 临界资源
不可以共享
临界资源
:一次只允许一个任务(进程、线程)访问的共享资源临界区
:访问临界资源的代码互斥机制
:mutex互斥锁,任务访问临界资源前申请锁,访问完后释放锁
临界资源的理解:
不能同时访问的资源,比如写文件,只能由一个线程写,同时写会写乱。
例如:外设打印机,打印的时候只能由一个程序使用。
外设基本上都是不能共享的资源。
在比如生活中的卫生间,同一时间只能由一个人使用
man手册找不到 pthread_mutex_xxxxxxx (提示No manual entry for pthread_mutex_xxx)的解决方法:
apt-get install manpages-posix-dev
四、互斥锁的创建与与销毁
1、互斥锁的创建
可以用
两种方法
创建互斥锁,静态方式
和动态方式
(1)动态方式:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
其中mutexattr
用于指定互斥锁属性
,如果为NULL
则使用缺省属性
。
(2)静态方式:
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
2、互斥锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex)
在Linux中,互斥锁并
不占用任何资源
,因此LinuxThreads
中的pthread_mutex_destroy()
除了检查锁状态
以外(锁定状态则返回EBUSY)没有其他动作。
意思就是一般不用
3、互斥锁的使用
(1)互斥锁的申请:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex)
成功
时返回0
,失败
时返回错误码
mutex
指向要初始化的互斥锁对象
pthread_mutex_lock
如果无法获得锁
,任务阻塞
pthread_mutex_trylock
如果无法获得锁,返回EBUSY
而不是挂起等待
(2)释放锁:
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
成功
时返回0
,失败
时返回错误码
mutex
:指向要初始化
的互斥锁对象- 执行完临界区要
及时释放锁
,方便其他线程访问
五、读写锁
必要性:提高线程执行效率
写者
:写者
使用写锁
,如果当前没有读者,也没有其他写者,写者立即获得写锁;否则写者将等待,直到没有读者和写者。
读者
:读者
使用读锁
,如果当前没有写者,读者立即获得读锁;否则读者等待,直到没有写者。
注意:
- 同一时刻只有
一个
线程可以获得写锁
,同一时刻可以有多个
线程获得读锁
。 读写锁
出于写锁状态
时,所有
试图对读写锁加锁的线程
,不管是读者试图加读锁,还是写者试图加写锁,都会被阻塞
。读写锁
处于读锁状态
时,有写者
试图加写锁时
,之后的其他线程的读锁请求会被阻塞
,以避免写者长时间的不写锁
pthread_rwlock_init //初始化一个读写锁
pthread_rwlock_rdlock //读锁定读写锁
pthread_rwlock_tryrdlock //非阻塞读锁定
pthread_rwlock_wrlock //写锁定读写锁
pthread_rwlock_trywrlock //非阻塞写锁定
pthread_rwlock_unlock //解锁读写锁
pthread_rwlock_destroy //释放读写锁
六、死锁
什么是死锁?
线程1
拥有资源1
,但同时也想拥有资源2
,线程2
拥有资源2
,但同时也想拥有资源1
,双方
都需要同时拥有资源1和资源2
才能释放资源,这时候就会进入死循环。
在看一个生活实例:
这就是死锁。
避免方法:
- 锁越少越好,最好使用一把锁
- 调整好锁的顺序
到这里就结束啦!