跟我一起学“仓颉”编程语言-线程通知之Monitor

跟我一起学“仓颉”编程语言-线程通知之Monitor 一、线程通信线程通信指的是线程之间的数据交换和同步控制。线程通信的目的是确保线程能够安全、高效的贡献数据和协调执行。条件变量是一种同步原语用于在并发编程中协调线程间的执行顺序。条件变量通常与互斥锁配合使用以确保对共享数据的安全访问和对待特定条件的有效检查。等待条件一个或多个线程可以在条件变量上等待直到某个条件成立。每个条件变量对应一个条件等待队列。通知操作另一个线程可以在条件变量上发出通知信号以唤醒一个或所有等待该条件的线程。package Study import std.sync.* // 创建线程通信 let monitor Monitor() // 条件是否满足的标记 var flag false main () { let future1 spawn { // 确保线程2先获得锁 sleep(Duration.second) synchronized(monitor) { println(线程1已创建) flag true // 调用notify函数通知线程2 monitor.notify() println(线程1执行结束) } } let future2 spawn { synchronized(monitor) { println(线程2已创建) while (!flag) { // 调用wait函数等待线程1的通知 monitor.wait() } println(线程2执行结束) } } future1.get() future2.get() }二、wait函数的调用放在特定条件的while循环中package Study import std.sync.* // 创建线程通信 let monitor Monitor() // 条件是否满足的标记 var flag false main () { let future1 spawn { // 确保线程2先获得锁 sleep(Duration.second) synchronized(monitor) { println(线程1已创建) // 调用notify函数通知线程2 monitor.notify() println(线程1执行结束) } } let future2 spawn { synchronized(monitor) { println(线程2已创建) // 调用wait函数等待线程1的通知 monitor.wait() println(线程2执行结束) } } future1.get() future2.get() }注意以上代码虽然结果相同但是线程2有可能被假唤醒线程在没有收到通知的情况下从等待状态意外醒来。为了正确处理多线程通知建议始终将wait函数的调用放在检查特定条件的while循环里。当一个Monitor实例调用了wait函数wait函数将完成如下步骤1. 添加当前线程到该Monitor实例对应的条件等待队列中2. 阻塞当前线程同时释放该Monitor实例的锁3. 等待其他线程通过同一个Monitor实例调用notify函数或notifyAll函数发送的通知或等待超时4. 当前线程被唤醒后会自动尝试重新获得锁。如果获取锁成功从wait函数的调用点继续执行同步代码块如果获取锁失败则当前线程被加入锁等待队列并被阻塞。三、notify函数和notifyAll函数不会释放锁在调用notify函数或notifyAll函数后当前线程仍然有锁。package Study import std.sync.* class ProducerConsumer { // 生产产品总数 let total: Int64 // 表示是否已经生产了产品且还未被消费 var flag: Bool false let monitor Monitor() init(count: Int64) { this.total count } // 创建一个线程用于生产 func produce() { spawn { for (i in 1..total) { synchronized(monitor) { while(this.flag) { // 等待消费者消费产品 monitor.wait() } // 生产产品 println(已生产产品编号: ${i}) this.flag true // 通知消费者产品已生产 monitor.notify() } } } } // 创建一个线程用于消费 func consume() { spawn { for (i in 1..total) { synchronized(monitor) { while(!this.flag) { // 等待生产者生产产品 monitor.wait() } // 消费产品 println(已消费产品编号: ${i}) this.flag false // 通知生产者产品已消费 monitor.notify() } } } } } main () { let producerConsumer ProducerConsumer(10) // 创建生产者 let producer producerConsumer.produce() // 创建消费者 let consumer producerConsumer.consume() producer.get() consumer.get() }在多线程中锁的获取顺序是由线程控制器控制因此结果是不确定的。package Study import std.sync.* class ProducerConsumer { // 生产产品总数 let total: Int64 // 表示是否已经生产了产品且还未被消费 var flag: Bool false let monitor Monitor() init(count: Int64) { this.total count } // 创建一个线程用于生产 func produce() { spawn { synchronized(monitor) { for (i in 1..total) { while(this.flag) { // 等待消费者消费产品 monitor.wait() } // 生产产品 println(已生产产品编号: ${i}) this.flag true // 通知消费者产品已生产 monitor.notify() } } } } // 创建一个线程用于消费 func consume() { spawn { synchronized(monitor) { for (i in 1..total) { while(!this.flag) { // 等待生产者生产产品 monitor.wait() } // 消费产品 println(已消费产品编号: ${i}) this.flag false // 通知生产者产品已消费 monitor.notify() } } } } } main () { let producerConsumer ProducerConsumer(10) // 创建生产者 let producer producerConsumer.produce() // 创建消费者 let consumer producerConsumer.consume() producer.get() consumer.get() }注意选择for-in循环内使用synchronized块还是synchronized块内使用for-in循环根据实际情况而选择总之对于使用Monitor的场景需要仔细控制何时进入等待状态以及何时唤醒其他线程。四、小结本章为大家详细的介绍了仓颉编程语言中线程通知之Monitor的内容下一章为大家带来线程通知之MultiConditionMonitor的内容。最后创作不易如果大家觉得我的文章对学习仓颉服务端开发有帮助的话就动动小手点个免费的赞吧收到的赞越多我的创作动力也会越大哦谢谢大家