今天主要学习了线程分离属性、线程互斥、死锁、信号量
线程分离属性:主要是让线程结束后,自动回收线程空间
pthread_attr_init
int pthread_attr_init(pthread_attr_t *attr);
功能:
线程属性初始化
pthread_attr_destroy
int pthread_attr_destroy(pthread_attr_t *attr);
功能:
线程属性销毁
pthread_attr_setdetachstate
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
功能:
设置分离属性
PTHREAD_CREATE_DETACHED 分离属性
PTHREAD_CREATE_JOINABLE 加入属性(默认)
练习:练习:定义一个学生结构体
struct student
{
char name[32];
char sex;
int age;
int score;
};
创建两个线程,线程1负责从终端接收学生信息,线程2负责将学生信息打印在终端
#include"head.h" //头文件
struct student{ //学生结构体
char name[20];
char sex;
int age;
int score;
};
void *thread1(void *arg) //线程1:接收信息
{
struct student *stu = arg;
fgets(stu->name,sizeof(stu->name),stdin);
stu->sex = getchar();
scanf("%d",&stu->age);
scanf("%d",&stu->score);
return NULL;
}
void *thread2(void *arg) //输出
{
sleep(10);
struct student *stu = arg;
printf("name:%s",stu->name);
printf("sex:%c\n",stu->sex);
printf("age:%d\n",stu->age);
printf("score:%d\n",stu->score);
return NULL;
}
int main(void)
{
pthread_t tid1; //线程1ID
pthread_t tid2; //线程2ID
struct student s;
pthread_create(&tid1,NULL,thread1,&s); //创建线程1
pthread_create(&tid2,NULL,thread2,&s); //创建线程2
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
return 0;
}
结果:
线程互斥
1.互斥锁:防止资源竞争
2.函数接口:
pthread_mutex_init:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
功能:
互斥锁初始化
参数:
mutex:互斥锁空间首地址
attr:互斥锁的属性(默认为NULL)
返回值:
成功返回0
失败返回错误码
pthread_mutex_destroy:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能:
互斥锁销毁
参数:
mutex:互斥锁空间首地址
返回值:
成功返回0
失败返回错误码
pthread_mutex_lock:
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:
上锁
pthread_mutex_unlock:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:
解锁
eg:
#include"head.h"
int val = 0;
int num1 = 0;
int num2 = 0;
pthread_mutex_t lock; //创建互斥锁
void *thread1(void *arg) //线程1:两个数自增
{
while(1)
{
pthread_mutex_lock(&lock); //上锁
num1 = val;
num2 = val;
pthread_mutex_unlock(&lock); //解锁
val++;
}
return NULL;
}
void *thread2(void *arg) //线程2:输出全局变量num1,num2
{
while(1)
{
pthread_mutex_lock(&lock);
if(num1 != num2)
{
printf("num1 = %d,num2 = %d\n",num1,num2);
}
pthread_mutex_unlock(&lock);
}
return NULL;
}
int main(void)
{
pthread_t tid1;
pthread_t tid2;
pthread_mutex_init(&lock,NULL); //互斥锁初始化
pthread_create(&tid1,NULL,thread1,NULL); //创建线程1
pthread_create(&tid2,NULL,thread2,NULL); //创建线程2
pthread_join(tid1,NULL); //回收线程空间,具有阻塞功能
pthread_join(tid2,NULL);
pthread_mutex_destroy(&lock); //互斥锁销毁
return 0;
}
结果:
因为线程与进程一样,宏观并行,微观串行,当无互斥锁时,当线程1进行到给num2赋值时,线程2进行比较并输入,则回输出出来,所以该练习可以体现互斥锁的重要性。
3.临界资源、临界区:
加锁解锁中间的代码称为临界资源、临界区
同一时刻临界资源不能同时执行,只能执行其中一个临界资源代码
原子操作:
CPU最小的一次不能被任务调度打断的操作称为原子操作
互斥锁只能解决资源竞争的问题,无法同步代码(没有先后执行的顺序关系)
死锁
多线程操作互斥锁,导致多个线程均无法向下执行的状态称为死锁状态简称为死锁
死锁产生的四个必要条件:
1.互斥条件
2.不可剥夺条件
3.请求保持
4.循环等待
避免产生死锁:
1.pthread_mutex_trylock 替代 pthread_mutex_lock
2.加锁顺序保持一致
信号量
信号量是一种资源,可以被初始化、申请、释放、销毁
P操作:申请资源
V操作:释放资源
sem_init
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:
初始化信号量
参数:
sem:信号量空间首地址
pshared:
0 一个进程中的所有线程间共享
非0 进程间共享
value:
初始化的值
返回值:
成功返回0
失败返回-1
sem_destroy:
int sem_destroy(sem_t *sem);
功能:
信号量的销毁
参数:
sem:信号量空间首地址
返回值:
成功返回0
失败返回-1
sem_wait:
int sem_wait(sem_t *sem);
功能:
申请信号量
sem_post:
int sem_post(sem_t *sem);
功能:
释放信号量
eg:线程1获取字符串,线程2输出
#include"head.h"
char tmpbuff[4096] = {0};
sem_t sem_w; //定义全局变量信号量
sem_t sem_r;
void *pthread1(void *arg) //线程1:获取字符串
{
while(1)
{
sem_wait(&sem_w); //申请w信号量,信号量-1
gets(tmpbuff);
sem_post(&sem_r); //释放r信号量,信号量+1
}
return NULL;
}
void *pthread2(void *arg) //线程2:输出字符串
{
while(1)
{
sem_wait(&sem_r); //申请r信号量,信号量-1
printf("tmpbuff = %s\n",tmpbuff);
sem_post(&sem_w); //释放w信号量,信号量+1
}
return NULL;
}
int main(void)
{
pthread_t tid1; //线程1ID
pthread_t tid2; //线程2ID
sem_init(&sem_w,0,1); //w信号量初始化,参数2中0代表一个进程中的所有线程间共享
//参数3中1代表:初始化的值为1,因为要先进行线程1,需要申请w信号量
sem_init(&sem_r,0,0);
pthread_create(&tid1,NULL,pthread1,NULL); //创建线程1
pthread_create(&tid2,NULL,pthread2,NULL); //创建线程2
pthread_join(tid1,NULL);
pthread_join(tid2,NULL);
sem_destroy(&sem_w); //信号量w销毁
sem_destroy(&sem_r); //信号量r销毁
return 0;
}