首页 > 其他分享 >【操作系统】同步

【操作系统】同步

时间:2024-12-29 23:55:21浏览次数:3  
标签:mu 同步 操作系统 int res queue 信号量 sem

同步(Synchronization)涉及到在多线程或多进程环境中协调多个执行线程的执行顺序,以确保数据的一致性和完整性。同步的目的是防止多个线程同时访问同一资源(如内存、文件、数据库等)时发生的冲突和数据不一致问题。

实现方法:

  • 互斥锁(Mutex):一种同步机制,用于保护共享资源不被多个线程同时访问。当一个线程获取了互斥锁,其他线程必须等待直到互斥锁被释放。

  • 信号量(Semaphore):一种计数器,用于控制对共享资源的访问数量。信号量可以用于控制同时访问特定资源的线程数量。

  • 条件变量(Condition Variable):与互斥锁一起使用的同步机制,允许线程在某些条件不满足时挂起(等待),并在条件满足时被唤醒。

 一、互斥锁和条件变量

一个简单的现打印A再打印B的同步程序:

package main

import (
	"fmt"
	"sync"
)

func main() {
	var mu sync.Mutex
	cond := sync.NewCond(&mu)
	var wg sync.WaitGroup
	wg.Add(1)
	flag := 0
	go func() {
		mu.Lock()
		defer mu.Unlock()
		if flag == 0 {
			cond.Wait()
		}
		fmt.Println("B")
		wg.Done()
	}()
	mu.Lock()
	fmt.Println("A")
	flag = 1
	cond.Signal()
	mu.Unlock()
	wg.Wait()
}

生产者消费者问题:

package main

import (
	"fmt"
	"math/rand/v2"
	"sync"
	"time"
)

type ShareRes struct {
	mu    sync.Mutex
	cond  sync.Cond
	queue []int //假设上限为10
}

func NewShareRes() *ShareRes {
	ret := &ShareRes{
		queue: make([]int, 0),
	}
	ret.cond = *sync.NewCond(&ret.mu)
	return ret
}
func product(res *ShareRes) {
	for {
		res.mu.Lock()
		for len(res.queue) > 10 {
			res.cond.Wait()
		}
		randInt := rand.IntN(100)
		res.queue = append(res.queue, randInt)
		fmt.Println("i am producer,i push ", randInt, " to queue")
		res.cond.Broadcast()
		res.mu.Unlock()
		time.Sleep(1 * time.Second)
	}
}
func consume(id int, res *ShareRes) {
	for {
		res.mu.Lock()
		for len(res.queue) == 0 {
			res.cond.Wait()
		}
		num := res.queue[0]
		res.queue = res.queue[1:]
		fmt.Println("i am comsumer,i get ", num, " from queue,id :", id)
		res.cond.Broadcast()
		res.mu.Unlock()
	}
}
func main() {
	var wg sync.WaitGroup
	wg.Add(1)
	res := NewShareRes()
	go product(res)
	for i := 0; i < 3; i++ {
		go consume(i, res)
	}
	wg.Wait()
}

二、信号量

Wait操作:这个操作会把信号量减去 1,相减后如果信号量 < 0,则表明资源已被占⽤,进程需阻塞等
待;相减后如果信号量 >= 0,则表明还有资源可使⽤,进程可正常继续执⾏。
Post操作:这个操作会把信号量加上 1,相加后如果信号量 <= 0,则表明当前有阻塞中的进程,于是
会将该进程唤醒运⾏;相加后如果信号量 > 0,则表明当前没有阻塞中的进程;

#include<semaphore.h>
#include<thread>
#include<iostream>
void ThreadFunc(void *arg){
    sem_t *sem=(sem_t *)arg;
    sem_wait(sem);//-1
    std::cout<<"B"<<"\n";
}

int main(){
    sem_t sem;
    sem_init(&sem,0,0);
    std::thread th(ThreadFunc,&sem);
    std::cout<<"A"<<"\n";

    sem_post(&sem);//+1
    th.join();
    sem_destroy(&sem);
    return 0;
}

具体过程:
如果进程 B ⽐进程 A 先执⾏了,那么执⾏到 P 操作时,由于信号量初始值为 0,故信号量会变为 -1,表示进
程 A 还没⽣产数据,于是进程 B 就阻塞等待;
接着,当进程 A ⽣产完数据后,执⾏了 V 操作,就会使得信号量变为 0,于是就会唤醒阻塞在 P 操作的进程
B;
最后,进程 B 被唤醒后,意味着进程 A 已经⽣产了数据,于是进程 B 就可以正常读取数据了。
可以发现,信号初始化为 0 ,就代表着是同步信号量,它可以保证进程 A 应在进程 B 之前执⾏。

相关函数:

1. sem_init

用于初始化一个匿名信号量。

int sem_init(sem_t *sem, int pshared, unsigned int value);
  • 参数
    • sem:指向信号量结构的指针。
    • pshared:共享标志位,0表示不共享(线程间同步),非0表示共享(进程间同步)。
    • value:信号量的初始值。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno
  • 说明:此函数用于初始化一个无名信号量,通常用于线程间的同步。

2. sem_destroy

用于销毁一个匿名信号量。

  • 函数原型
  • int sem_destroy(sem_t *sem);
  • 参数
    • sem:指向要销毁的信号量的指针。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno
  • 说明:此函数用于销毁一个已经初始化的匿名信号量。

3. sem_post

用于释放(增加)信号量的值。

  • 函数原型
  • int sem_post(sem_t *sem);
  • 参数
    • sem:指向具体信号量的指针。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno
  • 说明:此函数用于增加信号量的值,如果有进程或线程因等待信号量的值大于0而被阻塞,此函数会唤醒它们。

4. sem_wait

用于等待(减少)信号量的值。

  • 函数原型
  • int sem_wait(sem_t *sem);
  • 参数
    • sem:指向信号量的指针。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno
  • 说明:此函数用于减少信号量的值,如果信号量的值大于0,则立即返回;如果信号量的值为0,则阻塞等待,直到信号量的值变为大于0。

5. sem_trywait

用于尝试等待信号量的值,非阻塞。

  • 函数原型
  • int sem_trywait(sem_t *sem);
  • 参数
    • sem:指向信号量的指针。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno(例如EAGAIN表示信号量的值为0,无法减少)。
  • 说明:此函数用于尝试减少信号量的值,如果信号量的值大于0,则减少并返回成功;如果信号量的值为0,则返回失败,不会阻塞。

6. sem_getvalue

用于获取信号量的当前值。

  • int sem_getvalue(sem_t *sem, int *valp);
  • 参数
    • sem:指向信号量的指针。
    • valp:指向整数的指针,用于存储信号量的当前值。
  • 返回值
    • 成功:返回0。
    • 失败:返回-1,并设置errno
  • 说明:此函数用于获取信号量的当前值,并将其存储在valp指向的整数中。

标签:mu,同步,操作系统,int,res,queue,信号量,sem
From: https://blog.csdn.net/weixin_73809064/article/details/144808718

相关文章

  • 【操作系统】哲学家进餐问题
    目录一、概念二、以原子的思想解决死锁 三、破环环路的思想解决死锁四、使用管程来解决死锁一、概念问题描述:有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便......
  • HarmonyOS 5.0 Next实战应用开发—‘我的家乡’【HarmonyOS Next华为公司完全自研的操
    HarmonyOS5.0NextHarmonyOSNEXT是鸿蒙抛弃Linux内核及安卓开放源代码项目(AOSP)等代码的首个大版本,该系统仅支持鸿蒙内核和鸿蒙系统的应用,不再兼容安卓应用。引入了“和谐美学”设计理念,通过先进的物理渲染引擎,细腻地还原了真实世界的光影色彩与时空力感,为用户呈现更加沉浸、多......
  • 基于双PI控制器和三电平SVPWM交流同步直线电机矢量控制系统的simulink建模与仿真
    1.课题概述      基于PSO粒子群优化的PV光伏发电系统simulink建模与仿真。通过PSO粒子群优化进行最大功率跟踪。 2.系统仿真结果 3.核心程序与模型版本:MATLAB2022a  4.系统原理简介      光伏(Photovoltaic,PV)发电系统利用太阳能直接转换成电能,......
  • 【C语言】线程同步
    【C语言】线程同步线程同步1.互斥锁2.读写锁3.条件变量4.信号量线程同步  线程同步是指在多线程的情况下,如果多个线程去访问共享资源,需要按照一定规则顺序依次去访问,保证共享资源的数据一致性。1.互斥锁互斥相关函数//互斥量pthread_mutex_tmutex;//p......
  • 为什么要做纯血鸿蒙操作系统?
    在10月22日,华为最新的原生鸿蒙之夜暨全场景新品发布会上,余承东正式发布了“纯血鸿蒙”操作系统,这是继苹果iOS和安卓系统之后的全球第三大移动操作系统,同时也标志着国产操作系统实现了全面突破。(央视新闻报道https://mp.weixin.qq.com/s/IFatTQ7u7h92iGrr-xEedw)。那么我们为什么要......
  • 30天开发操作系统 第 9 天 -- 内存管理
    今天叙述很多,让大家理解一些内存及编译器的运行机制。内容不是很难,让我们开始吧!整理源文件`现在我们还残留一个问题,就是鼠标指针的叠加处理不太顺利。不过如果一味进行鼠标处理的话,大家可能很容易腻烦,所以我们今天干点儿别的。鼠标指针的叠加处理问题迟早会解决的,大家......
  • 操作系统模拟虚拟存储器的地址变换过程
    设计用于模拟快表、页表、地址变换所用的寄存器的数据结构;编制页表的初始信息文件,举例说明文件中具有的信息:共有5块,每块的状态、在内存和外存的起始地址等。编程实现虚拟存储器地址变换算法程序,动态输入所要访问的逻辑地址,变换过程文字描述以及变换后的物理地址;测试:输入......
  • CHCP(Change Code Page)命令源自 MS-DOS 操作系统,是用于显示或设置当前活动代码页(Code P
    chcp|MicrosoftLearnCHCP和相关说明,以下是按功能分类的表格:功能分类命令/选项说明查看当前活动代码页CHCP显示当前活动的代码页编号。设置活动代码页CHCP[nnn]设置指定的代码页编号,nnn 为要设置的代码页编号。进一步说明:查看当前活动代码页:CHCP......
  • c语言书籍排序 多数组协同排序 按价格排序【书名同步】 带有空格的字符串读取
    题目:编写程序,从键盘输入n(n<10)本书的名称和定价并存入结构数组中,按单价从小到大排序并输出排序后的书籍信息。输入输出示例:括号内为说明,无需输入输出输入样例:3(n=3)ProgramminginC21.5ProgramminginVB18.5ProgramminginDelphi20输出样例:Programmingin......
  • 基于Xxl-Job,dataX设计的数据同步和可视化任务编排工具
    使用vue3对xxl-job进行重构,并集成datax工具实现不同数据源的数据同步,支持glue模式,并新增存储过程调用,api任务调度和可视化任务编排,支持单任务-单任务串并联,单任务-任务集串并联和单任务-任务集-任务集串并联目前还只是1.0版本,会存在一些bug,想一起维护这个项目的小伙伴请联系我.........