首页 > 系统相关 >Linux多线程同步机制(下)

Linux多线程同步机制(下)

时间:2023-09-10 23:32:21浏览次数:40  
标签:同步 int Linux NULL cond mutex pthread 线程 多线程

@TOC

前言


一、读写锁

多线程同步机制中的读写锁(Read-Write Lock)是一种特殊的锁机制,用于控制对共享资源的读写访问。读写锁允许多个线程同时读取共享资源,但在写操作时需要独占访问。

读写锁的基本原则是:多个线程可以同时获取读锁,但只有一个线程可以获取写锁。当有线程持有写锁时,其他线程无法获取读锁或写锁,直到写操作完成并释放写锁。

读写锁有两种状态:读模式下加锁状态(读锁),写模式下加锁状态(写锁)。读写锁只有一把。

1. 读写锁的特性: 写独占,读共享。

  • 读写锁是 “ 写模式加锁 ” 时,解锁前,所有对该锁加锁的线程都会被阻塞。
  • 读写锁是 “ 读模式加锁 ” 时,如果线程以读模式 则对其加锁会成功;如果线程是以写模式加锁会阻塞。
  • 读写锁是 “ 读模式加锁 ” 时,既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞读模式请求。优先满足写模式加锁。读写锁并行阻塞,写锁优先级高。

2. 读写锁相关函数:

  • 初始化,pthread_rwlock_init
  • 获取读锁:使用pthread_rwlock_rdlock函数获取读锁。
  • 获取写锁:使用pthread_rwlock_wrlock函数获取写锁。
  • 释放锁:使用pthread_rwlock_unlock函数释放读锁或写锁。
  • 销毁读写锁:在不再需要时,使用pthread_rwlock_destroy函数销毁读写锁。

3. 示例代码: 下面代码设置一个写进程 和 三个读进程区访问共享资源。

(void*)i 将整数 i 转换为 void* 类型。在线程的函数中,需要将其转换回整数类型,以便使用它。可以使用 (int)arg 将 void* 转换回 int 类型。void* 是一种通用的泛型指针,可以在不关心具体类型的情况下进行转换和操作。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>
#include <unistd.h>

pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;		// 初始化读写锁

int data = 1;

void *write_thread (void *arg)
{
	while(1)
	{
		pthread_rwlock_wrlock(&rwlock);

		data += 1;
		printf("write : data = %d\n",data);	
		pthread_rwlock_unlock(&rwlock);

		sleep(rand() %3);					// 随机休眠,让出 cpu资源
	}
	pthread_exit(NULL);
}

void *read_thread (void *arg)
{
	int i = (int )arg;
	while(1)
	{
		pthread_rwlock_rdlock(&rwlock);
		
		printf("read %d : data = %d\n",i, data);	

		pthread_rwlock_unlock(&rwlock);
		sleep(rand() %3);
	}
	pthread_exit(NULL);
}


int main(void)
{
	pthread_t r_tid[5], w_tid;

	srand(time(NULL));

	// 创建多个读线程
    	for (int i = 0; i < 3; i++) 
	{
		 pthread_create(&r_tid[i], NULL, read_thread, (void*)i);
	}

 	pthread_create(&w_tid, NULL, write_thread, NULL);


	for(int j=0;j<3;j++)
	  pthread_join(r_tid[j],NULL);

	pthread_join(w_tid,NULL);

	pthread_rwlock_destroy(&rwlock);

	return 0;
}

Linux多线程同步机制(下)_条件变量

二、条件变量

在多线程编程中,条件变量是一种用于线程之间进行通信和同步的机制。条件变量允许一个线程等待特定的条件发生,并在条件满足时通知其他线程继续执行。条件变量本身不是锁。条件变量常与互斥锁(mutex)结合使用,以实现线程之间的同步操作。

1. 相关函数:

(1) 对条件变量进行初始化,并可指定属性。通常使用默认属性,可以传入 NULL。

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

(2) 销毁条件变量,释放相关资源。

int pthread_cond_destroy(pthread_cond_t *cond);
  • 当前线程等待条件变量满足,并释放关联的互斥锁。该函数会阻塞线程直至条件变量被通知。 在调用 pthread_cond_wait() 之前,必须先获得与条件变量关联的互斥锁 mutex 的锁,然后该函数会自动释放 mutex 的锁(自动 unlock),并让线程进入等待状态,直到被另一个线程通过 pthread_cond_signal() 或 pthread_cond_broadcast() 唤醒(自动 lock)。
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

(3) 通知等待在条件变量上的一个线程,使其继续执行。如果有多个线程等待,只通知其中一个线程。

int pthread_cond_signal(pthread_cond_t *cond);

(4) 通知所有等待在条件变量上的线程,使它们都继续执行。

int pthread_cond_broadcast(pthread_cond_t *cond) ;

2. 示例代码: 描述生产者 和 消费者的关系。

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

struct msg
{
	int num;
	struct msg* pnext;
};

struct msg *head = NULL;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


void *producer_thread (void *arg)							// 生产者线程
{
	while(1)
	{	
		struct msg *info = malloc(sizeof(struct msg));

		info->num = rand() % 1000 + 1;

		pthread_mutex_lock(&mutex);
		
		info->pnext = head;
		head = info;

		pthread_mutex_unlock(&mutex);
		
		// 唤醒阻塞的线程
		pthread_cond_signal(&cond);			
		printf("--------------------- producer : %d\n",info->num);
	
		sleep(rand() % 3);
	}
	return NULL;
}

void *customer_thread (void *arg)					// 消费者线程
{
	while(1)
	{
		struct msg *info;		

		pthread_mutex_lock(&mutex);
		
		if(head == NULL)
		{
			pthread_cond_wait(&cond, &mutex);			// 阻塞线程,等待有数据。
		}
		
		info = head;
		head = info->pnext;

		pthread_mutex_unlock(&mutex);

		printf("customer : =============== %d\n",info->num);

		free(info);
		info = NULL;

		sleep(rand() % 3);
	}
	return NULL;
}

int main(void)
{
	pthread_t pid,cid;

	srand(time(NULL));						// 设置随机种子

	int ret = pthread_create(&pid, NULL, producer_thread, NULL);
	if(ret != 0)
	{
		printf("prodecer_thread_create error\n");
	}

	ret = pthread_create(&cid, NULL, customer_thread, NULL);
	if(ret != 0)
	{
		printf("consumer_thread_create error\n");
	}

	pthread_join(pid, NULL);					// 等待回收线程,获取退出线程的状态
	pthread_join(cid, NULL);
	
	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);

	return 0;
}

总结

标签:同步,int,Linux,NULL,cond,mutex,pthread,线程,多线程
From: https://blog.51cto.com/u_16159289/7428873

相关文章

  • Linux权限管理
    用表格对比权限字符在文件和目录中的含义读取r写入w执行x允许查看文件内容、显示目录列表允许修改文件内容,允许在目录中新建、移动、删除文件或子目录允许运行程序、切换目录执行"chmodago=wmymkdir"命令的作用是什么?设置mymkdir所有用户、属组、其他用户的权限为可写。执......
  • Linux内核体系结构
    说明本章首先概要介绍了Linux内核的编制模式和体系结构,然后详细描述了Linux内核源代码目录中组织形式以及子目录中各个代码文件的主要功能以及基本调用的层次关系。接下来就直接切入正题,从内核源文件Linux/目录下的第一个文件Makefile开始,对每一行代码进行详细注释说明。本章内......
  • Linux:文件压缩解压gz、tar.gz、tar.xz、tar.bz2、tgz、zip
    (目录)tar#.tartar-xvfarchive.tartar.gz、tgz1、压缩tar-zcvf压缩文件名.tar.gz被压缩文件名#不保留文件路径tar-zcvf压缩文件名.tar.gz-C压缩前切换目录被压缩文件名参考如何在不保留目录结构的情况下tar目录?2、解压tar-zxvf压缩文件名.tar.gz#......
  • 安装Linux操作系统,学习Linux基础
    安装Linux操作系统,学习Linux基础1.操作过程2.GPT提问解决过程实录问题1:在配置Linux虚拟机时,命令行中输入sudoaptupgrade的结果是:E:Couldnotgetlock/var/lib/dpkg/lock-frontend-open(11:Resourcetemporarilyunavailable)E:Unabletoacquirethedpkgfronten......
  • 如何在 Kali Linux 上安装 SSH 服务
     目的我们的目的是Kali Linux 上安装SSH(安全 shell)。要求你需要有特权访问你的KaliLinux安装或者Live系统。困难程度很容易!惯例#-给定命令需要以root用户权限运行或者使用sudo命令$-给定命令以常规权限用户运行安装从终端使用apt-get命令安装SSH包......
  • 监听canal实现缓存同步
           ......
  • unix/linux系统编程第一、二章知识归纳
    1.引言1.1Unix&Linux简介及历史版本Unix和Linux是一系列强大的操作系统,具有丰富的历史和版本。Unix的初始版本由肯·汤普森(KenThompson)和丹尼斯·里奇(DennisRitchie)于20世纪70年代早期开发。它是一种通用操作系统,经典书目包括1988年的《TheCProgrammingLang......
  • Linux删除文件夹命令
    Linux删除文件夹命令在Linux系统中,要删除文件夹,可以使用以下命令:1.rm命令rm命令用于删除文件和目录。要删除一个空文件夹,可以使用以下命令:rm-rfolder_name其中,-r选项表示递归删除,即删除文件夹及其内部的所有文件和子文件夹。如果要删除非空文件夹,可以使用以下命令:rm-rffolder_......
  • Linux教材第一、二章学习笔记及遇到的问题
     第一章第一章主要学习了unix、Linux的特性、文件系统组织、系统管理等内容。UbuntuLinux的特性出于安全原因,要运行任何特权命令时,用户必须输入sudocommand,首先会验证用户的密码。 Unix/Linux文件系统组织目录的查看,创建,增加,删除 手册页的查看。 UbuntuLinux......
  • Linux下安装Redis的详细安装步骤
    一.Redis安装1.下载linux压缩包【redis-5.0.5.tar.gz】2.通过FlashFXP把压缩包传送到服务器3.解压缩tar-zxvfredis-5.0.5.tar.gz4.进入redis-5.0.5可以看到redis的配置文件redis.conf5.基本的环境安装使用gcc-v命令查看gcc版本已经是4.8.5了,于是就没有再次安装,直接......