4.7、线程间的同步互斥
程序员文章站
2023-12-25 23:56:09
...
线程操作优点:由于由全局变量(共用),可以在各个线程间访问相同空间,线程之间的通信比较方便
线程操作的缺点:线程间的同行需要采用同步互斥机制(线程操作随机执行,导致通信空间值的不确定性)
线程通信:采用同步互斥方式实现线程间的通信
一、同步:多个任务按照一定的先后顺序,协调工作,去执行一件事情(程序的相应代码)
用信号量(一个值)去实现同步;
由信号量去控制线程是阻塞还是继续执行
信号量:代表着一种资源数目,这个值最小为0
信号量:
1、申请资源//p操作(让线程往下执行)信号量-1
2、释放资源//v操作(通过信号量添加一个资源,提供线程往下执行的功能)信号量+1
p操作:
if(信号量-1){
线程往下执行
}
else{
等待申请资源
}
v操作:
释放一个资源:信号量+1
{
唤醒一个等待资源的线程让它继续执行
}
2、互斥:某个线程(地方)执行时,其他任何地方都不能执行,当前只有一个地方能够执行
通常情况用在资源竞争中,也是特殊的信号量
互斥锁的方式去实现
线程同步
- 信号量的初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
/****************************************************************
参数:1、指定设置的信号量是哪个(传入信号量的地址)
2、设置信号量在进程间操作还是线程间操作:设置为0表示线程间操作,非0表示进程间操作
3、设置信号量的初始值
返回值:成功返回0,失败返回-1
****************************************************************/
- 申请资源//p操作
int sem_wait(sem_t *sem);
/****************************************************************
参数:指定要操作哪个信号量执行申请资源(-1)
返回值:成功返回0,失败返回-1
****************************************************************/
- 释放资源//v操作
int sem_post(sem_t *sem);
/****************************************************************
参数:指定要操作哪个信号量执行释放资源(+1)
返回值:成功返回0,失败返回-1
****************************************************************/
线程互斥
- 初始化互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
/****************************************************************
参数:1、设置互斥锁的变量(容器),通过传变量的地址
2、设置互斥锁属性(通常设置为NULL表示默认方式)
返回值:返回值只有0
****************************************************************/
- 加锁(阻塞)
int pthread_mutex_lock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个
返回值:成功返回0失败返回非0
****************************************************************/
- 以非阻塞形式加锁(当执行加锁操作时,如果没有加锁成功,直接返回)
int pthread_mutex_trylock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个
返回值:成功返回0失败返回非0
****************************************************************/
- 解锁
int pthread_mutex_unlock(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个
返回值:成功返回0失败返回非0
****************************************************************/
- 销毁锁,让互斥锁变量不再代表锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
/****************************************************************
参数:指定要操作的互斥锁是哪个
返回值:成功返回0失败返回非0
****************************************************************/
- 池类算法
sched_yield()://出让调度器
在临界区中,为了不造成死锁,多注意临界区中的跳转(break、continue、goto、函数调用)语句和死循环语句
简单实例
/*****************************************************************
* Copyright (C) 2019 Sangfor Ltd. All rights reserved.
*
* 文件名称:thread_file.c
* 创 建 者:yinfei-hu
* 创建日期:2019-03-09 22:28:59
* 功能描述:文件:内容 1
* 20线程-》打开文件-》读数据-》往数据上+1,再写回去-》关闭-》退出
*
*****************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define THREADNUM 20
#define BUFSIZE 10
static int num = 0;
static pthread_mutex_t mut;
void *thread_fcun(void *arg){
FILE *fp;
char buf[BUFSIZE];
fp = fopen("./hello.txt","r+");
if(NULL == fp){
perror("fopen()");
pthread_exit(NULL);;
}
pthread_mutex_lock(&mut);//申请锁
fgets(buf,BUFSIZE,fp);//读
int num = atoi(buf);//转换
num ++;//加1
fseek(fp,0,SEEK_SET);//偏移
fprintf(fp,"%d",num);//写
fflush(fp);
pthread_mutex_unlock(&mut);//解锁
fclose(fp);
pthread_exit(NULL);
}
int main(){
int i,err;
pthread_t tid[THREADNUM];
pthread_mutex_init(&mut,NULL);//创建锁
for(int i = 0;i < THREADNUM;i++){
err = pthread_create(tid + i,NULL,thread_fcun,NULL);
if(err){
fprintf(stderr,"pthread_create():%s\n",strerror(err));
exit(1);
}
}
for(int i = 0;i < THREADNUM;i++){
pthread_join(tid[i],NULL);
}
pthread_mutex_destroy(&mut);
exit(0);
}
条件变量:pthread_cond_t
- 初始化条件变量
pthread_cond_init(条件变量指针,属性)
//初始化条件变量
//属性:NULL为默认属性
//使用宏初始化:pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
- 销毁条件变量
ptherad_cond_destroy(条件变量指针)
- 解锁等待通知,被唤醒后再申请锁
pthread_cond_wait(条件变量指针,互斥量指针)
- 解锁等待指定时间,如果在指定时间内都没有收到唤醒通知,自动醒来申请锁,此时函数立马返回ETIMEDOUT
pthread_cond_timedwait(条件变量指针,互斥量指针,超时时间)
设置超时2秒的方式:
struct timespec ts;
ts.tv_sec = time(0) + 2; //获取当前系统时间+2
ts.tv_nsec = 0; //此处设置纳秒为0
pthread_cond_timedwait(&cond,&mut,&ts);
- 唤醒所有的等待线程
pthread_cond_broadcast(条件变量指针)
- 唤醒任意一个等待的线程
pthread_cond_signal(条件变量指针)