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

简介: 【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
目录
相关文章
|
8月前
|
容器
多线程学习之生产者和消费者与阻塞队列的关系
多线程学习之生产者和消费者与阻塞队列的关系
29 0
|
10月前
|
安全 API C++
c++生产者和消费者线程循环
线程安全-生产者消费者模型
84 1
|
2月前
|
Java
用java实现生产者和消费者模式
用java实现生产者和消费者模式
38 1
|
12月前
|
算法
生产者消费者问题(生产者和消费者分别阻塞于不同的锁)
生产者消费者问题(生产者和消费者分别阻塞于不同的锁)
|
12月前
|
算法
生产者消费者问题(生产者和消费者都阻塞于同一把锁this锁)
生产者消费者问题(生产者和消费者都阻塞于同一把锁this锁)
|
安全 Java
【JUC基础】06. 生产者和消费者问题
学习JUC,就不得不提生产者消费者。生产者消费者模型是一种经典的多线程模型,用于解决生产者和消费者之间的数据交换问题。在生产者消费者模型中,生产者生产数据放入共享的缓冲区中,消费者从缓冲区中取出数据进行消费。在这个过程中,生产者和消费者之间需要保持同步,以避免数据出现错误或重复。今天我们就来说说生产者消费者模型,以及JUC中如何解决该模型的同步问题。
134 0
|
安全 数据处理
线程中的生产者和消费者模式
线程中的生产者和消费者模式
106 0
线程中的生产者和消费者模式
|
安全 Java
Java多线程——生产者/消费者问题
生产者/消费者问题
149 0