一、线程
1.1 pthread_cancel(线程的取消)
#include <pthread.h> -- 所需头文件
int pthread_cancel(pthread_t thread);
功能:取消线程
参数:
thread:要取消的线程的线程id
返回值;成功返回0,失败返回错误码
线程能否被取消还取决于线程的取消状态和取消类型:
#include <pthread.h> -- 所需头文件
inthread_setcancelstate(nt state,int *olestate);
1、设置线程为取消状态,默认为此状态
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL);
2、设置线程为不可取消的状态,设置之后,线程不会对其他线程发送的取消请求响应
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
int pthread_setcanceltype(int type,int *oldtype);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERROR,NULL);//默认为延时取消
//设置取消状态为立即取消,但是系统不保证可以立即取消
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
取消点:线程可被取消的节点
本质就是调用某些函数的时候,线程会检查是否有取消请求并相应。
比如系统调用,从内核空间返回用户空间的时候会检查是否有取消请求并相应。
线程取消代码示例:
#include <my_head.h>
pthread_t tid1,tid2;
void *thread_func(void *arg){
//设置线程为不可取消状态,线程无法被取消
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
while(1){
printf("111\n");
}
}
int main(int argc,const char *argv[]){
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
sleep(3);
pthread_cancel(tid1);
while(1);
return 0;
}
运行结果:手动停止。。
#include <my_head.h>
pthread_t tid1,tid2;
void *thread_func(void *arg){
// //设置线程为不可取消状态,线程无法被取消
// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL);
//立即取消,不保证成功
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
int a = 0;
while(1){
a++;
//printf("111\n");
}
}
int main(int argc,const char *argv[]){
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
sleep(3);
pthread_cancel(tid1);
while(1);
return 0;
}
运行结果:手动停止。。。
1.2 多线程访问同一变量问题
#include <my_head.h>
pthread_t tid1,tid2;//创建两个线程
int money = 0;//要访问的变量
void *thread_func1(void *arg){
//张三向银行存钱
int temp = 0;
for(int i = 0;i < 1000000;i++){
temp = money;
temp = temp+1;
money = temp;
}
}
void *thread_func2(void *arg){
//李四向银行存钱
int temp = 0;
for(int i = 0; i < 1000000;i++){
temp = money;
temp = temp+1;
money = temp;
}
}
int main(int argc,const char *argv[]){
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func1,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
ret = pthread_create(&tid2,NULL,thread_func2,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("银行最终存了%d元\n",money);
return 0;
}
运行结果:由于线程间运行方式是,时间片轮询,上下文切换。谁抢到谁就先运行会导致最终数据混乱。。。
1.3 互斥锁
1.3.1 互斥锁的概念
互斥锁是多线程对同一个变量访问的时候,同一时刻只能有一个线程对该全局变量进行访问操作。
在linux中,提供了一种实现互斥锁的操作的工具,叫做互斥锁。
在线程A获取到这把锁的时候,线程B是无法获取到锁的。
1.3.1 互斥锁的API
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);
功能:动态初始化互斥锁
参数:
mutex:互斥锁
attr:互斥锁属性 -- 填NULL即可,使用默认属性
返回值:成功返回0,失败返回错误码
互斥锁的静态初始化
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:获取互斥锁,如果所以京被其他程序获取,该线程会阻塞等待
参数:
mutex:要获取的锁
返回值:成功返回0,失败返回错误码
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:释放锁
参数:
mutex:要释放的锁
返回值:成功返回0,失败返回错误码
int pthread_mutex_destory(pthread_mutex_t *mutex);
功能:销毁锁
参数:
mutex:要销毁的锁
返回值:成功返回0,失败返回错误码
1.3.2 互斥锁的使用示例
#include <my_head.h>
pthread_t tid1,tid2;//创建两个线程
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
int money = 0;//要访问的变量
void *thread_func1(void *arg){
//张三向银行存钱
int temp = 0;
for(int i = 0;i < 1000000;i++){
pthread_mutex_lock(&mtx1);//上锁
temp = money;
temp = temp+1;
money = temp;
pthread_mutex_unlock(&mtx1);//释放锁
}
}
void *thread_func2(void *arg){
//李四向银行存钱
int temp = 0;
for(int i = 0; i < 1000000;i++){
pthread_mutex_lock(&mtx1);
temp = money;
temp = temp+1;
money = temp;
pthread_mutex_unlock(&mtx1);
}
}
int main(int argc,const char *argv[]){
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func1,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
ret = pthread_create(&tid2,NULL,thread_func2,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
printf("银行最终存了%d元\n",money);
//销毁锁
pthread_mutex_destroy(&mtx1);
return 0;
}
运行结果:
死锁问题:
死锁的概念:
死锁就是当某个线程想要获取一把锁,但是由于这把锁被其他线程长时间占用不释放,导致该线程长时间阻塞,无法向下执行。
死锁产生的情况:
- 线程使用锁之后不释放
- 有两把锁AB,线程A获取了锁A,线程B获取了锁B。然后线程A想要获取锁B,线程B想要获取锁A,就会导致死锁。
思索地解决办法:
- 用完锁之后及时释放,不要长时间持有锁。
- 按顺序加锁
- 避免嵌套使用锁
- pthread_mutex_timedlock根据时间加锁
- 在全局变量中指定所的状态,在获取锁之前先判断锁的状态,如果锁已经被占用,就不要尝试获取。
1.4 无名信号量(线程同步)
1.4.1 同步的概念
线程指的是让线程按照既定的顺序执行。
1.4.2 无名信号量API分析
#include <semaphore.h> --所需头文件
int sem_init(sem_t *sem,int pshared,unsigned int value);
功能:初始化无名信号量
参数:
sem:无名信号量
pshared:
0:用于线程间通信
非0:用于进程间通信(亲缘关系进程间通信)
value:信号量的初始值,大于等于0
初始化:成功返回0,失败返回-1,置位错误码
int sem_wait(sem_t *sem);
功能:获取信号量,让信号量的数值-1,如果信号量的数值已经是0,则阻塞线程
参数:
sem:信号量
返回值:成功返回0,失败返回-1,置位错误码
int sem_post(sem_t *sem);
功能:释放信号量,让数值+1
参数:
sem:信号量
返回值:成功返回0,失败返回-1,置位错误码
int sem_destory(sem_t *sem);
功能:销毁信号量
参数:
sem:信号量
返回值:成功返回0,失败返回-1,置位错误码
无名信号量使用示例
#include <my_head.h>
pthread_t tid1,tid2;
pthread_mutex_t mtx1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mtx2 = PTHREAD_MUTEX_INITIALIZER;
sem_t sem1;
sem_t sem2;
int cars = 10;
//生产者
void *thread_func1(void *arg){
while(1){
sem_wait(&sem2);
printf("生产者,生产一辆汽车\n");
pthread_mutex_lock(&mtx1);//获取锁
cars++;
printf("现有%d辆汽车\n",cars);
pthread_mutex_unlock(&mtx1);
sem_post(&sem1);//释放信号量
}
}
//消费者
void *thread_func2(void *arg){
while(1){
sem_wait(&sem1);
printf("消费者,买了一辆汽车\n");
cars--;
pthread_mutex_lock(&mtx1);//获取锁
printf("现有%d辆汽车\n",cars);
pthread_mutex_unlock(&mtx1);
sem_post(&sem2);//释放信号量
}
}
int main(int argc,const char *argv[]){
//信号量初始化
sem_init(&sem1,0,10);
sem_init(&sem2,0,0);
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func1,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
ret = pthread_create(&tid2,NULL,thread_func2,NULL);
if(0 != ret){
printf("pthread_create error:%s\n",strerror(ret));
return -1;
}
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
pthread_mutex_destroy(&mtx1);
pthread_mutex_destroy(&mtx1);
return 0;
}
运行结果:
1.5 条件变量(线程同步)
1.5.1 分析API
#include <pthread.h>
int pthread_cond_init(pthread_cond_t *cond,const pthread_condattr_t *att);
功能:初始化条件变量
参数:
cond:条件变量
attr:条件变量属性--传NULL使用默认属性
返回值:成功返回0,失败返回错误码
othread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_wait(pthread_cond_t *cond,pthread_muntx_t *muntex);
功能:等待条件变量
参数:
cond:要等待的条件变量
mutex:互斥锁
返回值:成功返回0,失败返回错误码
1.先获取互斥锁;pthread_munex_lock
2.调用pthread_cond_wait函数
01:将线程加入等待队列
02:释放锁
03:休眠
04:获取锁
05:退出休眠
int pthread_cond_signal(pthread_cond_t *cond);
功能:保证唤醒一个正在等待的cond条件变量通知的线程
参数:
cond:条件变量
返回值:成功返回0,失败返回错误码
int pthread_cond_broadcast(pthread_cond_t *cond);
功能:唤醒所有正在等待的线程
参数:
cond:条件变量
返回值:成功返回0,失败返回错误码
int pthread_cond_destory(pthread_cnd_t *cond);
功能:销毁条件变量
参数:
cond:条件变量
返回值:成功返回0,失败返回错误码
1.5.2 条件变量使用示例
一个生产者多个消费者
#include <my_head.h>
pthread_t tid1,tid2;
pthread_cond_t cond;//定义条件变量
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int flag = 0;
//生产者
void *thread_func1(void *arg){
while(1){
pthread_mutex_lock(&mtx);
if(0 != flag)
pthread_cond_wait(&cond,&mtx);
printf("我生产了一辆汽车\n");
flag = 1;
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond);//保证唤醒一个线程
}
}
void *thread_func2(void *arg){
while(1){
pthread_mutex_lock(&mtx);
if(0 == flag)
pthread_cond_wait(&cond,&mtx);
printf("消费者买了一辆汽车\n");
flag = 0;
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mtx);
}
}
int main(int argc,const char *argv[]){
//初始化条件变量
pthread_cond_init(&cond,NULL);
int ret = 0;
ret = pthread_create(&tid1,NULL,thread_func1,NULL);
if(0 != ret){
printf("pthread_cretae error:%s\n",strerror(ret));
return -1;
}
ret = pthread_create(&tid2,NULL,thread_func2,NULL);
if(0 != ret){
printf("pthread_cretae error:%s\n",strerror(ret));
return -1;
}
pthread_join(tid1,NULL);
//回收线程资源
pthread_join(tid2,NULL);
return 0;
}
运行结果:
总结:
无名信号量1,适用于对限定的数量资源的同步访问操作。一般使用无名信号量做少量线程间的同步。
条件变量是根据条件的变化去实现线程间同步操作。
条件变量适用于大量线程间的同步操作。