大话通道

2024-06-12 15:38
文章标签 通道 大话

本文主要是介绍大话通道,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

稍微有编码常识的同学,都会意识到程序并非完全按照纯代码逻辑顺序执行。有多线程多进程经验,知道程序执行往往表现的像无规律的交叉,而且每次重新来过,还体现不一样。 本文以通道为引子,意直白讲述并发同步

内存顺序

编译器(在编译时刻)优化和CPU处理器优化(运行时),会调整指令的执行地顺序。这导致指令执行顺序与代码指定的顺序不完全一致。所以当你认为你的代码是按照书写的逻辑执行时,事实有可能并非如此,尤其是在高并发的情况下。在go语言中表现为,指令执行顺序(指令顺序,称之为内存顺序)的调整,可能会影响到其它协程行为。

并发同步

为了应对这些调整,需要同步技术。换而言之,对于某些调整代码会影响到最终输出结果的情况,必须作出内存顺序保证。非go语言通常会用到锁和原子操作,以及适用于多个进程之间或多个主机之间,用网络或文件读写来实现并发同步。
锁基本上有这几种。互斥锁有点独的味道,无论是读还是写,都会阻塞,即有人在读其它人别说写了,想看看(读)都没门。读写锁,单写多读模型,除了写时阻塞,你读时,我可读,他可读,大家谁读都没问题,不阻塞。更进一步,其实站在锁的立场,它需要知道,谁在用他,用完后给哪些需要他的人。换而言之,得通知可能得锁人,让那些没得锁的人继续等待。条件锁出现了,它依赖于前面两种锁,通常用来协调想要访问共享资源的线程,在对应的共享资源状态发送变化时,通知其它因此而阻塞(在等待)的线程。

中断 代码从运行状态切换到非运行状态称之为中断
原子操作通常是 CPU 和操作系统提供支持,执行过程中不会中断

原子操作 互斥锁只能保证临界区代码的串行执行,不能保证代码执行的原子性。
而原子执行过程中不中断,可以完全消除竞态条件,从而绝对保证并发安全性,无锁直接通过CPU指令直接实现。

顺序保证

上面说了那么多,就一目的实现内存模型(指令执行)顺序保证,即确保某些情况下顺序不被调整(或即便调整了也不影响最终的结果正确性)。Go内存模型除了提供主流的锁或原子操作做出顺序保证外,还提供了通道操作顺序保证。

通道

通道简单理解,就是由读,写,缓冲三个队列组成的数据类型。其实它的设计逻辑,以无缓冲通道为例,假定有一空杯,大家喝水都用它,加锁那套是要喝水的人时不时要看看,有没有人在用那个杯子,没有人用它用完则放回原地。而通道不一样,它是杯子到手了,我用完了,直接传递给下一个要用的人,当然你得保证喝完之后杯子里有水。二者的区别在于,前者需要锁防止大家争抢水杯,而后者则你不需要去找水,你只需要告诉水杯我要喝水,上一个喝水的人,喝完之后,会灌满递给你。前者强调共享,后者重在传递。所以不要让计算通过共享内存来通讯,而应该让它们通过通讯来共享内存

最快到达

现实生活中,发出请求并不总是及时响应,有时面对多源数据,我们会发出多个请求,只采用其中响应最快的那个。

import ("fmt""math/rand""time")func main() {rand.Seed(time.Now().UnixNano())startTime := time.Now()// 采用缓冲通道,模拟同步发出多个请求c := make(chan int32, 5)for i := 0; i < cap(c); i++ {go source(c)}// 只取一个最快的响应结果rnd := <-c// 测量最快时间差fmt.Println(time.Since(startTime))fmt.Println(rnd)}func source(c chan<- int32) {ra, rb := rand.Int31(), rand.Intn(3)+1// 随机模拟请求的响应时间time.Sleep(time.Duration(rb) * time.Second)c <- ra}

Future/Promise

Future/promise 常常用在请求/回应场合,以下示例 sumSquares 函数调用的两个实参请求并发进行。 每个通道读取操作将阻塞到请求返回结果为止。 两个实参总共需要大约3秒钟(而不是6秒钟) 准备完毕(以较慢的一个为准)

package mainimport ("fmt""math/rand""time")func longTimeRequest() <-chan int32 {r := make(chan int32)go func() {time.Sleep(time.Second * 3)   // 模拟一个工作负载r <- rand.Int31n(100)     // 随机正整数范围}()return r}func sumSquares(a, b int32) int32 {return a*a + b*b}func main() {rand.Seed(time.Now().UnixNano())   // 准备随机初始种子start := time.Now()   // 计时a, b := longTimeRequest(), longTimeRequest()  // goroutine分发,并发执行fmt.Println(sumSquares(<-a, <-b), time.Since(start))  	 // 输出类似 10084 3.000541298s}
  • 通知

互斥锁

将容量为1的缓冲通道,作为互斥锁,下面示例发送操作加锁

package main
import "fmt"func main() {mutex := make(chan struct{}, 1) // 容量必须为1,二元信号counter := 0increase := func() {mutex <- struct{}{} // 发送通道加锁counter++<-mutex // 解锁}increase10 := func(done chan<- struct{}) {for i := 0; i < 10; i++ {increase()}done <- struct{}{}}done := make(chan struct{})go increase10(done)go increase10(done)<-done; <-donefmt.Println(counter) // 20
}

计数信号量

计数信号量经常被使用于限制最大并发数,下面以酒吧喝酒示例

package mainimport ("log""math/rand""time")type Seat inttype Bar chan Seatfunc (bar Bar) ServeCustomer(c int, seat Seat) {log.Print("顾客#", c, "进酒吧了")log.Print("++ 顾客", c, "坐在",seat,"号位开始喝酒#", )time.Sleep(time.Second * time.Duration(2+rand.Intn(6)))log.Print("-- 顾客#", c, "离开了", seat, "号座位")bar <- seat    // 离开座位}func main() {rand.Seed(time.Now().UnixNano())bar24x7 := make(Bar, 3)    // 此酒吧最多能同时服务3个客人for seatId := 0; seatId < cap(bar24x7); seatId++ {bar24x7 <- Seat(seatId)    // 酒吧放置3把椅子,此处不会阻塞}for customerId := 0; ; customerId++ {time.Sleep(time.Second)seat := <-bar24x7 // 等待,当有空位时允许进go bar24x7.ServeCustomer(customerId, seat)  } select {}   // 主协程永久阻塞,防止退出 }  
  • 对战

其它

用通道实现请求/应答模式,使用缓冲,并不能保证结果顺序与分发顺序一致。道理很简单,在同步发出多个请求,最先响应的并不一定是第一个请求(各个请求响应耗时不一),你不能根据响应的结果来断定通道是否读取完毕。读写一致是最好的保证。

这篇关于大话通道的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1054628

相关文章

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

Matplotlib图像读取和输出及jpg、png格式对比,及透明通道alpha设置

图像像素值 图像像素值一般size为3,也就是通道数,分别代表R,G,B,如果只有单一 一个值则表示灰度值,也就是说一张二维图片,当长和宽都为1080时,那么若是灰度图像,图像尺寸为(1080,1080,1)若是RGB图像则为(1080,1080,3), jpg、png图像格式 jpg图像的灰度值范围和RGB范围为[0,255],数值类型为uint8,也就是无符号整数 png图像的灰度值范

大话C++:第6篇 命名空间namespace作用域

1 命名空间概述 在一个大型的软件项目中,可能会有许多不同的代码文件,这些文件可能由不同的开发者编写,或者来自不同的库和模块。如果这些代码文件中存在同名的变量、函数、类或其他标识符,那么在编译或运行时就可能发生命名冲突,导致程序无法正确执行。 通过使用命名空间(namespace),开发者可以将相关的代码、变量、函数等组织在一起,形成一个独立的命名空间。这样,即使不同的代码片段中使用了相同的标

flutter开发多端平台应用的探索 下 (跨模块、跨语言通信之平台通道)

前文 Flutter 是一个跨平台的开发框架,它允许开发者使用相同的代码库来构建 iOS、Android、Web 和桌面应用程序。 上文flutter开发多端平台应用的探索 上(基本操作)-CSDN博客列举了一些特定平台的case(桌面端菜单,鼠标快捷键)的使用方法,有些是flutter提供了对应能力,只需要学习如何调API,有些事三方库支持,本文要探讨的平台通道是更为强大的工具,很多三方插件

【go 通道】go语言通道channel

通过使用通道,在多个goroutine发送和接受共享的数据,达到数据同步的目的。 通道,他有点像在两个routine之间架设的管道,一个goroutine可以往这个管道里塞数据,另外一个可以从这个管道里取数据,有点类似于我们说的队列。 声明一个通道很简单,我们使用chan关键字即可,除此之外,还要指定通道中发送和接收数据的类型,这样我们才能知道,要发送什么类型的数据给通道,也知道从这个通道里可

大话实时数据平台设计(上)

点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 一、相关概念背景 1从现代数仓架构角度看实时数据平台 现代数仓由传统数仓发展而来,对比传统数仓,现代数仓既有与其相同之处,也有诸多发展点。首先我们看一下传统数仓(图1)和现代数仓(图2)的模块架构: 图1 传统数仓 图2

大话实时数据平台设计(下)

在上篇点击上方蓝色字体,选择“设为星标” 回复”资源“获取更多资源 大数据技术与架构 点击右侧关注,大数据开发领域最强公众号! 暴走大数据 点击右侧关注,暴走大数据! 实时数据平台(RTDP,Real-time Data Platform)是一个重要且常见的大数据基础设施平台。在上篇中,我们从现代数仓架构角度和典型数据处理角度介绍了RTDP,并探讨了RTDP的整体设计架构。 本文作为下

docker安装 redis 并且加密开启SSL/TLS通道

拉取镜像 docker pull registry.cn-hangzhou.aliyuncs.com/qiluo-images/redis:latestdocker tag registry.cn-hangzhou.aliyuncs.com/qiluo-images/redis:latest redis:latest 要在 Docker 容器中启动 Redis 并开启 SSL/TLS 加密

庞峰Opencv学习(一)--BGR与通道的概念

1.  cvCreateImage()--cvCreateImage(size, IPL-DEPTH_X, Channel_num) size描述了图像的大小,IPL_DEPTH_X描述了颜色深度,Channel_num描述了图像的通道数。 对于传统的RGB三色图,其实就是一个三通道(R,G,B),每个通道通过8位无符号数(0-255种颜色)来表示。但是与传统的RGB表示不同,在Opencv

YOLOv8改进实战 | 引入混合局部通道注意力模块MLCA(2023轻量级)

YOLOv8专栏导航:点击此处跳转 前言 YOLOv8 是由 YOLOv5 的发布者 Ultralytics 发布的最新版本的 YOLO。它可用于对象检测、分割、分类任务以及大型数据集的学习,并且可以在包括 CPU 和 GPU 在内的各种硬件上执行。 YOLOv8 是一种尖端的、最先进的 (SOTA) 模型,它建立在以前成功的 YOLO 版本的基础上,并引入了新的功能和改进