【Linux】——线程同步与互斥

本文介绍了Linux中线程同步与互斥的概念及四种主要的同步方式:互斥锁、条件变量、读写锁和信号量。通过具体实例阐述了如何使用这些机制保证线程安全和避免饥饿问题,提高资源访问的合理性。
摘要由CSDN通过智能技术生成

1、同步和互斥的概念

为了更加清楚的了解这两个的概念。我们举个例子来说明为什么在有了互斥机制的情况下还需要有同步机制。

  • 假设学校有一间自习室只有一个位置,那有一天你准备去学校自习室上自习,起的很早去拿了钥匙开门进去开始自习,但是在自习了一分钟之后,突然不想自习了想出去玩一会,走到门口刚刚准备把钥匙挂在门上又想得好好学习啊,于是又打开门进了自习室,刚刚坐下一分钟又想出去玩了,于是又出来之后一想还是学习吧,又打开门进去,此时门口其实已经等了一大批人准备在这个教室自习,可以你就不停的打开门进去自习一分钟又出来又打开门进去自习一分钟,重复的做这件事,如此反复导致门口所有等着自习的人均无法进行自习,这就导致了饥饿问题,于是老师在自习室门口贴了一条规定,在你进去自习出来之后如果还想接着自习就必须到教室外面排队等待自习,这样就保证了所有的同学都能够得到自习的机会。

从这个例子中我们就可以发现,拿走钥匙不让其他人入内其实就是一种互斥机制,但是为了解决饥饿问题,后来加入了排队机制,其实就是一种同步的机制。
所以,我们可以认为,互斥机制是为了保证安全,同步机制是为了保证合理性

同步
在保证数据安全的前提下,让线程能够按照某种特定的访问顺序访问临界资源,从而有效的避免饥饿问题。
互斥
一个公共资源同一时刻只能被一个进程或线程使用,多个进程或线程不能同时使用公共资源

2、线程同步方式

线程同步方式和进程的很像,都是对临界资源进行控制,处理线程互斥,同步这两种关系。主要有四种方式:互斥量,读写锁,信号量,条件变量

2.1互斥锁(同步)

1、概念
在多任务的操作系统中,同时运行的多个任务可能都需要使用同一资源。互斥锁是一种简单的加锁方法来控制对共享资源的访问
互斥锁只有两种状态,线程在进入临界区之前,加锁操作。线程在退出临界区之后,解锁操作

1.1使用步骤

  • 线程在访问临界资源之前,对临界区代码进行加锁操作如果锁是加锁状态的,则线程执行加锁操作将被阻塞,直到锁解锁可解除解锁。即表示这块临界区只允许一个线程进行访问
  • 进行临界区代码执行,即对临界资源进行操作的代码运行。
  • 退出临界区之后,进行解锁操作
    可以概括为:
    在这里插入图片描述

2、互斥锁的类型和方法
互斥变量用pthread_mutex_t数据类型来表示。一般的,将锁定义到全局

  1. 初始化一个互斥锁
int pthread_mutex_init(pthread_mutex_t *mutex,const pthread_mutexattr_t *attr);

第一个参数将定义的互斥锁地址传入即可。一般的,第二个参数传为空,表示用默认的属性初始化互斥量。
初始化的锁是处于解锁状态

  1. 加锁和解锁

对互斥量进行加锁,需要调用pthread_mutex_lock,如果互斥量已经上锁,调用线程将阻塞直到互斥量被解锁,对互斥量解锁,需要调用pthread_mutex_unlock.具体的方法实现如下:

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);

【注意!】因为上面lock这种加锁操作如果锁是加锁状态就会阻塞.
所以有一种trylock的方式尝试对互斥量进行加锁,如果互斥量处于未锁住状态,那么trylock将锁住互斥量,不会出现阻塞并返回0。否则就会复制失败,不能锁住互斥量,而返回EBUS,具体实现如下:

int pthread_mutex_trylock(pthread_mutex_t *mutex);
  1. 销毁锁
int pthread_mutex_destroy(pthread_mutex_t *mutex);

3、互斥锁的特点

  • 原子性:把互斥量锁定为一个原子操作
  • 唯一性:如果一个线程锁定了一个互斥量,在它解除锁定之前,没有其他线程可以多动这个互斥量
  • 非繁忙等待:如果一个线程已经锁定了一个互斥量,第二个线程又试图去锁定这个互斥量,则第二个线程将被挂起(不占用任何cpu资源),直到第一个线程解除对这个互斥量的锁定为止,第二个线程则被唤醒并继续执行,同时锁定这个互斥量。
  • 加锁,解锁在代码中必须成对出现,即一个线程加锁,解锁;不能一个线程加锁,一个线程解锁,会容易出现死锁。
  • 一把锁只能用于对一个资源的互斥访问,不能实现多个资源的多线程互斥问题。

5、实例
实现:
主线程负责接收用户输入,函数线程负责将用户输入打印到终端界面
分析:

  • 数据传递:线程之间需要共享数据,所以不能定义为局部的,必须定义为全局,堆都可以,我们使用全局数组buff来存储数据。
  • 线程关系:主线程没有接收到数据时,函数线程不能打印;函数线程打印时,主线程不能读取;主线程写入时,函数线程不能打印。

总结:buff是临界资源,主线程和函数线程对其进行访问,所以在处理buff前进行加锁操作,处理完成后解锁。

代码实现如下:

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

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

pthread_mutex_t mutex;

char buff[128] = {
   0};

void *fun(void *arg)
{
   
	while(1)
	{
   
		pthread_mutex_lock(&mutex);
		if(strncmp(buff,"end",3) == 0)
		{
   
			break;
		}

		printf("fun :%s\n",buff);
		memset(buff,0,128);

		int n = rand() % 3 +1;
		sleep(n);

		pthread_mutex_unlock(&mutex);
	    n = rand() % 3 + 1;
		sleep(n);
	}
}

int main()
{
   
	srand((unsigned int)(time(NULL) * time(NULL)));
	pthread_mutex_init(&mutex,NULL);//初始化的锁是解锁状态的

//创建一个线程
	pthread_t id;
	int res = pthread_create(&id,NULL,fun,NULL);
	assert(res == 0);

	while(1)
	{
   
		pthread_mutex_lock(&mutex);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值