华为仓颉语言初识:并发编程之同步机制(下)

简介: 本文介绍了华为仓颉语言中的三种线程同步机制:MultiConditionMonitor、synchronized和ThreadLocal。MultiConditionMonitor继承自ReentrantMutex,通过条件变量实现复杂线程同步,文中以生产者-消费者模型为例展示了其用法。synchronized关键字自动加解锁,简化了ReentrantMutex的使用。ThreadLocal则通过线程局部存储实现线程隔离。这三种机制分别适用于不同场景,与Java中的同步工具类似,掌握后可以有效解决多线程并发问题。文章包含代码示例和测试结果,清晰地展示了各机制的实现原理和使用方法。

前言

华为仓颉语言除了提供原子操作,可重入互斥锁和 Monitor 用来保证多线程并发安全之外,还提供了 MultiConditionMonitor,synchronized 和 ThreadLocal 三种同步机制解决线程间同步问题。本篇文章详细介绍这三种同步机制的作用原理及使用,建议点赞收藏!

同步机制

MultiConditionMonitor

MultiConditionMonitor 继承于可重入互斥锁 ReentrantMutex,并提供了一个 newCondition() 方法用来动态创建条件变量,解决复杂场景下线程间同步问题。

以经典的生产-消费模型为例,看看 MultiConditionMonitor 是怎么实现生产者和消费者的?

  1. 定义共享资源类,创建两个条件变量 empty 和 full,用来标识 MulticonditionMonitor 的等待和唤醒条件。
class Apple {
    let monitor = MultiConditionMonitor()
    var count: Int64 = 0
    var empty: ConditionID
    var full: ConditionID
    init() {
        count = 0
        synchronized(monitor) {
            empty = monitor.newCondition()
            full = monitor.newCondition()
        }
    }
    func produceApple() {
        synchronized(monitor) {
            while (count == 100) {
                 AppLog.info("Apple-produceApple wait")
                monitor.wait(empty)
            }
            count++
            AppLog.info("Apple-produceApple ${count}")
            monitor.notify(full)
        }
    }
    func comsumApple() {
        synchronized(monitor) {
            while (count == 0) {
                  AppLog.info("Apple-comsumApple wait")
                monitor.wait(full)
            }
            count--;
               AppLog.info("Apple-comsumApple ${count}")
            monitor.notify(empty)
        }
    }
}
  1. 测试生产-消费者模型,当一个线程生产一个 apple 后,另一个线程则消费掉苹果,否则当前线程处于等待状态。
spawn {
        for (_ in 1..8) {
             apple.produceApple()
             sleep(Duration.millisecond * 200) 
        }
     }
  spawn {
        for (_ in 1..8) {
            apple.comsumApple()
            sleep(Duration.millisecond * 200) 
       }
  }
  1. 测试结果,Apple 的生产与消费交替执行。
Apple-produceApple 1
Apple-comsumApple 0
Apple-produceApple 1
Apple-comsumApple 0
Apple-produceApple 1
Apple-comsumApple 0
Apple-produceApple 1
Apple-comsumApple 0
Apple-produceApple 1
Apple-comsumApple 0

synchronized

synchronized 关键字对于大家来说肯定不陌生,在 java 中,synchronized 用来给共享变量进行加锁确保多线程下对共享变量的访问安全。而在仓颉语言中,synchronized 通常和 ReentrantMutex()一起使用,用来自动加解锁。

不使用 synchronized 时,需要手动调用 lock()和 unlock()方法。

var sum = AtomicInt64(0)
  let mutex =  ReentrantMutex()
  for (pattern in 1..100) {
         spawn {
            mutex.lock()
            sum +=1
             mutex.unlock()
            }
         }
  sleep(Duration.second*2)
  AppLog.info("Main===${sum}")

使用 synchronized 后,不需要手动调用lock()和 unlock()方法。

var sum = AtomicInt64(0)
  let mutex =  ReentrantMutex()
  for (pattern in 1..100) {
         spawn {
             synchroized(mutex){
              sum +=1
             }
            }
         }
  sleep(Duration.second*2)
  AppLog.info("Main===${sum}")

ThreadLocal

线程局部变量 ThreadLocal 的作用在仓颉与 Java 中基本相同,都是将数据保存在线程内部存储空间来保存局部变量,使不同的线程间能够安全的访问自己的局部变量,做到线程隔离的作用。

let threadLocal = ThreadLocal<Int64>()
       let fun1 =  spawn {
             this.threadLocal.set(100)
               AppLog.info("线程1 === ${this.threadLocal.get().getOrThrow()}")
                        }
let fun2=  spawn {
         this.threadLocal.set(200)
         AppLog.info("线程2 === ${this.threadLocal.get().getOrThrow()}")
                        }
//输出
 线程1 === 100
 线程2 === 200

总结

仓颉语言中的一共提供了 6 种并发工具,用来解决多线程下的并发安全问题。本篇文章讲述最后的三种并发工具,使用和理解上都和 java 基本相似,特别是synchroized 和 ThreadLocal 的基本使用掌握起来也十分容易,已经学会了的小伙伴,赶快动手试试吧!。

目录
相关文章
|
存储 缓存 文件存储
如何保证分布式文件系统的数据一致性
分布式文件系统需要向上层应用提供透明的客户端缓存,从而缓解网络延时现象,更好地支持客户端性能水平扩展,同时也降低对文件服务器的访问压力。当考虑客户端缓存的时候,由于在客户端上引入了多个本地数据副本(Replica),就相应地需要提供客户端对数据访问的全局数据一致性。
32706 79
如何保证分布式文件系统的数据一致性
|
前端开发 容器
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局(上)
HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第8章FlexBox布局
17759 20
|
设计模式 存储 监控
设计模式(C++版)
看懂UML类图和时序图30分钟学会UML类图设计原则单一职责原则定义:单一职责原则,所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。bad case:IPhone类承担了协议管理(Dial、HangUp)、数据传送(Chat)。good case:里式替换原则定义:里氏代换原则(Liskov 
36689 20
设计模式(C++版)
|
存储 编译器 C语言
抽丝剥茧C语言(初阶 下)(下)
抽丝剥茧C语言(初阶 下)
|
机器学习/深度学习 人工智能 自然语言处理
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
带你简单了解Chatgpt背后的秘密:大语言模型所需要条件(数据算法算力)以及其当前阶段的缺点局限性
24767 14
|
机器学习/深度学习 弹性计算 监控
重生之---我测阿里云U1实例(通用算力型)
阿里云产品全线降价的一力作,2023年4月阿里云推出新款通用算力型ECS云服务器Universal实例,该款服务器的真实表现如何?让我先测为敬!
36667 15
重生之---我测阿里云U1实例(通用算力型)
|
SQL 存储 弹性计算
Redis性能高30%,阿里云倚天ECS性能摸底和迁移实践
Redis在倚天ECS环境下与同规格的基于 x86 的 ECS 实例相比,Redis 部署在基于 Yitian 710 的 ECS 上可获得高达 30% 的吞吐量优势。成本方面基于倚天710的G8y实例售价比G7实例低23%,总性价比提高50%;按照相同算法,相对G8a,性价比为1.4倍左右。
|
存储 算法 Java
【分布式技术专题】「分布式技术架构」手把手教你如何开发一个属于自己的限流器RateLimiter功能服务
随着互联网的快速发展,越来越多的应用程序需要处理大量的请求。如果没有限制,这些请求可能会导致应用程序崩溃或变得不可用。因此,限流器是一种非常重要的技术,可以帮助应用程序控制请求的数量和速率,以保持稳定和可靠的运行。
29841 52