前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Rust并发控制之Semaphore-两线程交替打印

Rust并发控制之Semaphore-两线程交替打印

作者头像
newbmiao
发布于 2023-11-27 04:33:41
发布于 2023-11-27 04:33:41
49800
代码可运行
举报
文章被收录于专栏:学点Rust学点Rust
运行总次数:0
代码可运行

信号量(Semaphore)是一种对资源并发访问控制的方式。

区别于互斥锁(Mutex)是对共享资源的独占访问,Semaphore 允许指定多个并发访问共享资源。

就是说 Semaphore 像一个持有令牌(permit/token)的桶,每一个并发访问需要持有(acquire)一个令牌来访问共享资源,

当没有令牌时,没法访问共享资源,直到有新的令牌加入(add)或者原来发出的令牌放回(release)桶中。

接下来,我们尝试用通过用它来实现两个线程交替打印 1 和 2,来更直观了解如何使用 semaphore

Rust std 库中没有正式发布的 semaphore(std::sync::Semaphore 在 1.7.0 废弃了)。下边用 tokio 库提供的 semaphore

首先安装 tokio 库

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
# 手动添加tokio到cargo.toml
# 或使用cargo-add: cargo add tokio --features sync,macros,rt-multi-thread
[dependencies]
tokio = { version = "1.34.0", features = ["sync", "macros", "rt-multi-thread"] }

先来一版常规实现,初始化一个只有一个令牌的 semahore,两个线程去并发持有令牌,用后释放(通过 drop)令牌,实现交替打印

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use std::sync::Arc;
use tokio::sync::Semaphore;

#[tokio::main]
async fn main() {
    let semaphore = Arc::new(Semaphore::new(1));
    let cnt = 3;
    let semaphore2 = semaphore.clone();

    let t1 = tokio::spawn(async move {
        for _ in 0..cnt {
            let permit = semaphore.acquire().await.unwrap();
            print!("1 ");
            // 可不写,离开scope时自动释放,放回令牌桶
            drop(permit);
        }
    });

    let t2 = tokio::spawn(async move {
        for _ in 0..cnt {
            // 或用 _ ignore返回值,即时回收令牌
            let _ = semaphore2.acquire().await.unwrap();
            print!("2 ");
        }
    });

    tokio::try_join!(t1, t2).unwrap();
}

乍看没什么问题,但是打印其实不一定是1 2 1 2 1 2的顺序。

原因很简单,我们只是约束了令牌同时只能有一个线程获取到,但是没有约束谁先谁后啊。所以其实没有实现交替打印。

怎么交替打印呢?

要控制顺序,我们可以让每个线程所持有的 semaphore 里的令牌时动态增加和消耗,然后一个令牌桶数量的增加滞后于另一个。

增加可以用 add_permits, 消耗后不放回可以用 forgot, 代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
use std::sync::Arc;
use tokio::sync::Semaphore;

#[tokio::main]
async fn main() {
    // 线程1的令牌桶1初始一个令牌,可以先打印1
    let semaphore = Arc::new(Semaphore::new(1));
    let cnt = 3;
    let semaphore2 = semaphore.clone();

    // 线程2的令牌桶2初始没有令牌,直到1打印后增加令牌
    let semaphore_wait = Arc::new(Semaphore::new(0));
    let semaphore_wait2 = semaphore_wait.clone();

    let t1 = tokio::spawn(async move {
        for _ in 0..cnt {
            let permit = semaphore.acquire().await.unwrap();
            print!("1 ");
            // 消耗令牌,不放回令牌桶1
            permit.forget();
            // 令牌桶2增加令牌,可以打印2
            semaphore_wait2.add_permits(1);
        }
    });

    let t2 = tokio::spawn(async move {
        for _ in 0..cnt {
            let permit = semaphore_wait.acquire().await.unwrap();
            print!("2 ");
            // 消耗令牌,不放回令牌桶2
            permit.forget();
            // 令牌桶1增加令牌,可以打印1
            semaphore2.add_permits(1);
        }
    });

    tokio::try_join!(t1, t2).unwrap();
}

通过两个动态的令牌桶(semaphore)线程的执行顺序就能交替执行了。

可以和上篇 condvar 实现的版本 对比下, 感受下 semaphore 的魅力。


推荐阅读

如果有用,点个 在看,让更多人看到

外链不能跳转,戳 阅读原文 查看参考资料

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 菜鸟Miao 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
vuex里mapState,mapGetters使用详解
3. 在 src 目录下创建 store.js 文件,并在 main.js 文件中导入并配置
庞小明
2019/07/27
9.4K0
10.Vuex组件中的mapState、mapGetters、mapMutations、mapActions等辅助函数
可以将所有的Mutation事件,写入到一个单独的文件中,然后通过常量来替代,可以方便开发者对项目中所有Mutation
全栈程序员站长
2022/09/01
1.3K0
了解Vuex状态管理模式的理解强化指南
Vuex是什么呢?它是Vue的状态管理模式,在使用vue的时候,需要在vue中各个组件之间传递值是很痛苦的,在vue中我们可以使用vuex来保存我们需要管理的状态值,值一旦被改变,所有引用该值的地方就会自动更新。是不是很方便,很好用呢?
桃翁
2019/11/22
1.2K0
Vuex的简单入门
在src目录下新建个store文件夹,里面新建index.js 在index.js文件夹中创建Vuex实例
明知山
2020/09/03
3120
vuex学习笔记
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
前端迷
2018/10/22
7440
vuex学习笔记
Vuex详细介绍
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。这是官网的说法,其实很简单:就是一个加强版的data! 在单页应用中会有一个data函数,里面就存放了当前页面的一些数据。比如:
从入门到进错门
2019/06/11
1K0
10分钟搞懂 vuex
  这个状态我们可以理解为在data中的属性,需要共享给其他组件使用的部分。   也就是说,是我们需要共享的data,使用vuex进行统一集中式的管理。
我不是费圆
2020/10/23
4850
最详细的Vuex教程
1、利用npm包管理工具,进行安装 vuex。在控制命令行中输入下边的命令就可以了。
全栈程序员站长
2022/06/27
9320
​轻松掌握vuex,让你对状态管理有一个更深的理解
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
前端老鸟
2019/07/29
3.4K0
一文让你彻底搞懂 vuex,满满的干货
简单地讲:可以把多个组件都需要的变量全部存储到一个对象里面,然后这个对象放在顶层的 vue 实例中,让其他组件可以使用。这样多个组件就可以共享这个对象中的所有属性。
呆呆
2021/12/01
9000
(Vue全家桶)Vue-vuex
vuex是一个专门为vue.js设计的集中式状态管理架构。状态?我把它理解为在data中的属性需要共享给其他vue组件使用的部分,就叫做状态。
楠楠
2018/09/11
8960
前端成神之路-Vuex
Vuex是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间的数据共享
海仔
2021/05/06
1.5K0
Vuex的简单使用
简而言之,Vuex采用类似全局对象的形式来管理所有组件的公用数据,如果想修改这个全局对象的数据,得按照Vuex提供的方式来修改(不能自己随意用自己的方式来修改)。
不愿意做鱼的小鲸鱼
2022/09/24
4490
Vuex的简单使用
vuex简单理解
官方的说法是Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
煎饼
2022/12/13
4300
vue之vuex2.0使用详解
Vuex 的核心是 store, 它是一个通过 Vuex.Store 构造函数生成的对象。为什么它会是核心呢?因为我们调用这个构造函数创建store 对象的时候,给它传递参数中包装了state, mutation , action 等核心内容。Vuex 的思想是 当我们在页面上点击一个按钮,它会处发(dispatch)一个action, action 随后会执行(commit)一个mutation, mutation 立即会改变state,state改变以后我们的页面会从state获取数据从而改变页面。Store对象包含了我们谈到的所有内容:action, state, mutation,所以是核心了。
山行AI
2019/07/30
1.7K0
vue之vuex2.0使用详解
vuex知识点集合
vuex 是一个专门为vue.js 应用程序开发的状态管理模式。它采用集中式储存管理应用的所有组件的状态,并以响应的规则保证状态以一种可预测的方式发生变化
xyzzz
2020/12/20
6470
Vuex简介
Vuex 是一个专门为 Vue.js 应用程序开发的状态管理模式。它用于管理应用程序中的数据状态,并使得状态在不同组件之间共享和响应变化变得更加简单和可预测。
堕落飞鸟
2023/05/21
6130
Vuex状态管理总结
3、Vuex 应用的核心是 store(仓库)-- 包含 state(组件中的共享状态)和 mutations(改变状态的方法)
Leophen
2019/08/23
5570
Vue基础知识巩固之全面了解Vuex,比官方更易懂(上)
写项目很久了,偶尔用到Vuex也是用一些很浅显的功能,就是简单的存储一下用户信息,用的时候取一下,很少深入的使用,现在静下心来想给自己写个项目,在写的过程中,顺便把以往忽略的基础知识学习巩固一下,这篇文章就是记录一下学习Vuex的知识,既然是巩固知识,那那些基础的就直接一笔带过了。
十里青山
2022/08/07
8360
Vue基础知识巩固之全面了解Vuex,比官方更易懂(上)
Vue 全家桶学习笔记:Vuex
开发中,多个组件很可能会共享同一个状态(包括状态和数据),而组件和组件之间可能是兄弟关系,也可能是复杂的多层嵌套关系,如果单靠组件通信完成状态变更和同步,事情会变得很麻烦:
Chor
2019/11/26
7100
Vue 全家桶学习笔记:Vuex
相关推荐
vuex里mapState,mapGetters使用详解
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验