可重入锁实现消费者和生产者的例子

简介: 【6月更文挑战第28天】本文探讨了Python和Go中使用可重入锁(RLock)进行线程同步以及异步操作。异步存取示例展示了goroutine的并发优势,启动简单且运行异步。goroutine的调度和并发处理能力是其高效并发的关键。

1 简介

我们通过可重入锁在python的同步线程中实现了 对线程异步的操作, 一个可重入锁必须由获得它的线程来释放。 一旦一个线程获得了一个可重入锁,同一个线程可以再次获得它而不阻塞。

而该线程每获得一次锁,就必须释放一次锁。 获取的锁都必须释放一次。 它获取一个信号,将内部计数器递减1。

当调用没有参数时:如果内部计数器在进入时大于 0,则将其递减1并立即返回。
如果它是零则阻塞,等待其他线程调用release(),使其大于零。

这是以适当的互锁方式进行的,因此 如果多个acquisition()调用被阻塞,release()将正好唤醒其中一个唤醒它们中的一个。

实现可能会随机选择一个,所以被阻塞的线程被唤醒的顺序为 唤醒被阻塞线程的顺序不应该被依赖。

在这种情况下,没有在这种情况下没有返回值。
当调用阻塞设置为 "true "时,做与调用 "无参数 "时相同的事情。 不带参数的情况下做同样的事情,并返回true。

当调用时将阻塞设置为false时,不要阻塞。如果一个没有参数的调用会阻塞,立即返回false;

否则,做与无参数调用时相同的事情,并返回true。

当调用时的超时时间不是None,它将阻塞最多 超时秒。
如果在这个时间段内,获取没有成功完成时间间隔内没有成功完成,则返回false。 否则返回true。
Toroid托瑞德符号.png

可重入锁允许线程递归获取锁,每次获取后需相应释放。在Python中,acquire()release()管理内部计数器,确保正确同步。线程安全的队列在生产者(同步存入)和消费者(异步取出)间协调数据传输,展示出不同线程行为导致的输出无序性。Go中,goroutines作为轻量级协程,通过channel进行同步通信。

2 同步存入和异步取出

使用 入口判断 代码执行 同步还是异步,producer 同步 存入

   def producer(q, n):
        for i in range(n):
            q.put(i)
        q.put(None)

   q = Queuey(2)

启用线程同步存入

  Thread(target=producer, args=(q, 10)).start()
  time.sleep(1)

启用线程循环,异步取出

  loop.run_until_complete(aconsumer(q)) 

运行时的输出,可以看到由于同步线程和异步线程的不同操作 输出的无序的:

        put sync item: 0
        put sync item: 1
        put sync item: 2
    get async item
    Async Got: 0
    get async item
    put sync item: 3
    Async Got:put sync item:  14
    get async item
    Async Got: 
    put sync item: 2
    get async item
    5
    Async Got: put sync item: 3
    6
    get async item
    Async Got: put sync item: 4
    7
    get async item
    Async Got: put sync item: 5
    8
    get async item
    Async Got: put sync item: 6
    9
    get async item
    Async Got:put sync item:  7None

    get async item
    Async Got: 8
    get async item
    Async Got: 9 

3 同步存,异步取:go的简单例子

同步存入时需要注意,把channel的长度与调用者匹配

 func SyncPutin(n int) {
    AsyncQueue = make(chan int, n)
    for i := 0; i < n; i++ {
        AsyncQueue <- i
    }
  }

在消费时取出即可

    func Customer(n int) {
        wg.Add(1)
        go func() {
            var count int
            for {
                fmt.Println("get async item")
                if count >= n {
                    wg.Done()
                    os.Exit(1)
                }
                count += 1
                newOne := <-AsyncQueue
                fmt.Println("Async Got:", newOne)
            }
        }()
        wg.Wait()
    }

运行输出

        get async item
    Async Got: 0
    get async item
    Async Got: 1
    get async item
    Async Got: 2
    get async item
    Async Got: 3
    get async item
    Async Got: 4
    get async item
    Async Got: 5
    get async item
    Async Got: 6
    get async item
    Async Got: 7
    get async item
    Async Got: 8
    get async item
    Async Got: 9

4 异步存入 和 异步取出

使用异步库的队列,存取先后顺序并不重要。 异步取 异步存

   from asyncio import Queue

    q = Queue()
    loop.create_task(aconsumer(q))  
    loop.run_until_complete(aproducer(q, 10))   

实际的输出

  python async_third.py

    Async Got: 0
    Async Got: 1
    Async Got: 2
    Async Got: 3
    Async Got: 4
    Async Got: 5
    Async Got: 6
    Async Got: 7
    Async Got: 8
    Async Got: 9

5 异步存取 go的简单例子

go 的异步存入使用 内置的匿名函数即可

  func PutsIn(n int) {
        go func() {
            for i := 0; i < n; i++ {
                AsyncQueue <- i
            } 
        }() 
    }

消费者保持不变

    func Customer(n int) {
        wg.Add(1)
        go func() {
            var count int
            for {
                fmt.Println("get async item")
                if count >= n {
                    wg.Done()
                    os.Exit(1)
                }
                count += 1
                newOne := <-AsyncQueue
                fmt.Println("Async Got:", newOne)
            }
        }()
        wg.Wait()
    }

运行查看输出

  go run main.go

    get async item
    Async Got: 0
    get async item
    Async Got: 1
    get async item
    Async Got: 2
    get async item
    Async Got: 3
    get async item
    Async Got: 4
    get async item
    Async Got: 5
    get async item
    Async Got: 6
    get async item
    Async Got: 7
    get async item
    Async Got: 8
    get async item
    Async Got: 9

6 小结

协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方在切回来的时候,恢复先前保存的寄存器上下文和栈。

因此,协程能保留上一次调用时的状态 也就是说:

        进入上一次离开时所处逻辑流的位置。
        线程和进程的操作是由程序触发系统接口,最后执行者是系统
        协程的操作执行子则是用户自身程序,goroutine也是协程

goroutine 是Golang语言中最经典的设计,也是其魅力所在,goroutine本质是协程 是实现并行计算的核心。

goroutine 使用方式非常简单,只需要使用go关键字即可启动一个协程并且它是处于异步方式运行的,你不需要等它运行完成以后再执行以后的代码。

通过go 关键字启动一个协程来运行函数。

    go func() {}()

groutine能拥有强大的并发实现是通过GPM调度模型实现。

使用go的特性与python的经典一般用法进行比较似乎不太公平,这里只做简单对比,了解其优点.

完整代码:

    https://github.com/hahamx/examples/blob/main/alg_practice/1_pys_async/asyncio_cmp.py
目录
相关文章
|
Linux Android开发
AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解
AVB源码学习(六):AVB2.0 Device Mapper和Dm verity详解
1247 0
|
程序员
程序员的自我修养—链接、装载与库--书签目录PDF
程序员的自我修养—链接、装载与库--书签目录PDF
1643 0
|
10月前
|
人工智能 程序员 API
iOS|记一名 iOS 开发新手的前两次 App 审核经历
啥,这玩意也有新手保护期?
288 0
|
JavaScript
Vue2进度条(Progress)
这篇文章介绍了如何在Vue 3框架中创建一个可自定义设置的进度条组件,包括进度条的宽度、当前进度、线条宽度和是否显示进度信息等属性。
600 2
Vue2进度条(Progress)
|
安全 网络协议 Shell
Github代码仓库SSH配置流程
这篇文章是关于如何配置SSH以安全地连接到GitHub代码仓库的详细指南,包括使用一键脚本简化配置过程、生成SSH密钥对、添加密钥到SSH代理、将公钥添加到GitHub账户以及测试SSH连接的步骤。
698 0
Github代码仓库SSH配置流程
|
JavaScript API 容器
Vue3气泡卡片(Popover)
这是一个基于Vue的气泡卡片组件(Popover)介绍,提供了在线预览链接及详细API参数说明,包括maxWidth、title、content等,并支持自定义样式。
646 0
Vue3气泡卡片(Popover)
|
分布式计算 并行计算 负载均衡
并行计算与NumPy:加速数据处理的新途径
【4月更文挑战第17天】本文探讨了使用NumPy结合并行计算加速数据处理的方法。并行计算通过利用多个处理单元提高处理大规模数据的效率,而NumPy虽不直接支持并行计算,但可与OpenMP、Cython或Dask等工具结合。OpenMP能并行化NumPy函数,Dask则用于分布式并行计算。同时,文中提醒注意数据划分、通信开销、并行化策略及负载均衡等问题。随着技术发展,未来并行计算将在数据处理领域发挥更大作用。
|
JavaScript 前端开发 NoSQL
go embed 实现gin + vue静态资源嵌入
go embed 实现gin + vue静态资源嵌入
975 0
|
算法 大数据 API
合约跟单API对接火币/币安/OK交易所一键跟单系统开发策略详细/案例项目/技术分析/代码部署
     量化合约指的是目标或任务具体明确,可以清晰度量。根据不同情况,表现为数量多少,具体的统计数字,范围衡量,时间长度等等。所谓量化就是把经过抽样得到的瞬时值将其幅度离散,即用一组规定的电平,把瞬时抽样值用最接近的电平值来表示。经过抽样的图像,只是在空间上被离散成为像素(样本)的阵列。而每个样本灰度值还是一个由无穷多个取值的连续变化量,必须将其转化为有限个离散值,赋予不同码字才能真正成为数字图像。这种转化称为量化。