linux应用开发基础知识(六)——互斥锁和条件变量

一些基础概念

临界资源: 任一时刻只允许一个线程访问的共享资源
临界区: 访问临界资源的代码
原子性: 不会被任何调度机制打断的操作,该操作只有两态:要么完成,要么未完成。
互斥: 任何时刻,互斥可以保证有且只有一个执行流进入临界区,访问临界资源,通常对临界资源起保护作用,而锁就是实现互斥的。
同步: 同步是一种机制,用于协调不同进程、线程或设备之间的操作,确保它们按照预期的顺序和方式进行。同步的目的是保持数据的一致性和系统的稳定性。

互斥锁

线程之间共享同一地址空间和资源,而线程的并发执行也可能导致资源竞争的问题。为了解决线程之间资源竞争的问题,linux系统提供了互斥锁(Mutex)这一机制。
在使用互斥锁的过程中,当一个线程要访问共享资源时,首先会尝试获取互斥锁。如果锁已被其他线程占用的,则该线程会被阻塞,直到其他线程释放锁为止;而当线程访问完共享资源后,需要显式地释放互斥锁,以便其他线程可以继续访问共享资源。
使用互斥锁的一些API函数

#include <pthread.h>
#include <time.h>

pthread_mutex_t local_mutex;//定义锁

// 初始化一个互斥锁。后面那个参数是他的属性。
int pthread_mutex_init(pthread_mutex_t &mutex, const pthread_mutexattr_t *attr);

// 对互斥锁上锁,若互斥锁已经上锁,则调用者一直阻塞,
// 直到互斥锁解锁后再上锁。
int pthread_mutex_lock(pthread_mutex_t *mutex);

// 调用该函数时,若互斥锁未加锁,则上锁,返回 0;
// 若互斥锁已加锁,则函数直接返回失败,即 EBUSY。
int pthread_mutex_trylock(pthread_mutex_t *mutex);

// 当线程试图获取一个已加锁的互斥量时,pthread_mutex_timedlock 互斥量
// 原语允许绑定线程阻塞时间。即非阻塞加锁互斥量。
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,
const struct timespec *restrict abs_timeout);

// 对指定的互斥锁解锁。
int pthread_mutex_unlock(pthread_mutex_t *mutex);

// 销毁指定的一个互斥锁。互斥锁在使用完毕后,
// 必须要对互斥锁进行销毁,以释放资源。
int pthread_mutex_destroy(pthread_mutex_t *mutex);

互斥锁的阻塞模式

#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<time.h>
char *buf[5];
int pos;
pthread_mutex_t mutex;
void* task(void *p)
{
	pthread_mutex_lock(&mutex);
	buf[pos]=(char*)p;
	pos++;
	pthread_mutex_unlock(&mutex);
}
int main()
{
	pthread_t id1,id2;
	pthread_mutex_init(&mutex,NULL);
	pthread_create(&id1,NULL,task,(void *)"zhangsan" "yaowenwen");
	pthread_create(&id2,NULL,task,(void *)"lisi");
	pthread_join(id1,NULL);
	pthread_join(id2,NULL);
	pthread_mutex_destroy(&mutex);
	for(int i=0;i<pos;i++)
	{
		printf("%s",buf[i]);
	}
	printf("\n");
	return 0;
}

可以看到主函数创建了两个线程,两个线程调用的是同一个函数,作用均为向一个全局数组内写入字符。
在那个函数内调用了加锁函数和解锁函数。即当第一个线程执行时,第二个线程必须等待第一个线程执行完毕。最后结果如下:
在这里插入图片描述

条件变量

互斥量可以防止多个线程同时访问临界资源,而条件变量允许一个线程将某个临界资源的状态变化通知其他线程,在共享资源设定一个条件变量,如果共享资源条件不满足,则让线程到该条件变量下阻塞等待,当条件满足时,其他线程可以唤醒条件变量阻塞等待的线程。(也就是说可以防止一个线程一只占着某一个资源,其他线程共享不了资源)

在线程之间有一种情况:线程A需要某个条件才能继续往下执行,如果该条件不成立,此时线程A进行阻塞等待,当线程B运行后使该条件成立后,则唤醒该线程A继续往下执行。

在pthread库中,可以通过条件变量中,可以设定一个阻塞等待的条件,或者唤醒等待条件的线程。

怎么使用条件变量

条件变量的接口

pthread_cond_t 是 POSIX 线程库(Pthreads)中用于表示条件变量的数据类型。

 条件变量的初始化
#include <pthread.h>//头文件
方式一(pthread_cond_t是局部全局都可以用):
int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
方式二(pthread_cond_t是全局变量时):
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
注意:restrict 是一个类型限定符,它用于告知编译器两个指针不会指向同一个内存位置,这样编译器可以生成更高效的代码
参数
cond:一个指向 pthread_cond_t 类型的指针,用于存储初始化后的条件变量。cond只有两种取值:0、1
attr:一个指向 pthread_condattr_t 类型的指针,用于指定条件变量的属性。通常可以传递 NULL(nullptr),以使用默认属性。
返回值:
如果成功,返回 0。
如果失败,返回错误码。
/*pthread_cond_t  cond; 变量cond只有两种取值1、0。*/

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);             
 //功能:初始化一个条件变量

 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);         
/*
功能:阻塞等待一个条件变量,也就是等待信号的发起
函数作用:
阻塞等待条件变量cond(参1)满足
释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);
*/
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, 
const struct timespec *restrict abstime);     //功能:限时等待一个条件变量

int pthread_cond_signal(pthread_cond_t *cond);           //功能:唤醒至少一个阻塞在条件变量上的线程

int pthread_cond_broadcast(pthread_cond_t *cond);     //功能:唤醒全部阻塞在条件变量上的线程

int pthread_cond_destroy(pthread_cond_t *cond);        // 功能:销毁一个条件变量
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_mutex_t lock;


pthread_cond_t cond;

void*Run(void*arg)
{
    while(1){
    char*Str=(char*)arg;

    pthread_mutex_lock( &lock ) ;
    pthread_cond_wait(&cond,&lock);//默认不满足条件,线程会阻塞等待条件变量就绪
    printf("%s is open\n" , Str) ;
     
    pthread_mutex_unlock( &lock ) ;
    sleep(4) ;
    	
}
}

int main()
{
  pthread_mutex_init(&lock,NULL);//初始化锁
  pthread_cond_init(&cond,NULL);//初始化条件变量
  pthread_t tid1,tid2;
  pthread_create(&tid1,NULL,Run,(void*)"tid1");
  pthread_create(&tid2,NULL,Run,(void*)"tid2");

  while(1)
  {
    
    pthread_cond_broadcast(&cond);//唤醒线程
    sleep(2) ;
  }

  pthread_mutex_destroy(&lock);
  pthread_cond_destroy(&cond);
  return 0;
}

在给信号那行代码处,如果给的是一个线程,那么"tid1 is open",出现之后,再经过两秒“tid2 is open”才会出现。
如果给信号处,是给所有线程信号,那么"tid1 is open"和“tid2 is open”同时出现,再经过两秒,又再次同时出现。
在这里插入图片描述
具体执行过程:运行主函数,初始化锁,初始化条件变量。然后创建两个线程tid1,tid2。这两个线程均调用run函数,run函数内部先上锁,然后就是等待阻塞,等到主函数内给出来了信号再继续执行。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值