大话通道

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

相关文章

linux设备上的Onvif 实现10:获取支持通道的RTSP地址

1 背景 前文已经判断出了支持的通道,对于主通道子通道都支持的摄像头,优先选择主通道获取流媒体地址。实际应用中只要取得一个通道的RTSP即可。 2 GetStreamUri 获取流媒体地址的函数是GetStreamUri,我的版本中是: SOAP_FMAC5 int SOAP_FMAC6 soap_call___ns8__GetStreamUri(struct soap *soap, co

【PyTorch】【机器学习】图片张量、通道分解合成和裁剪

一、导入所需库 from PIL import Imageimport torchimport numpy as npimport matplotlib.pyplot as plt 二、读取图片 pic = np.array(Image.open('venice-boat.jpg')) 上述代码解释:先用Image.open()方法读取jpg格式图片,再用np.array()方法

PhotoShop-通道

通道: 1、  通道分类: (1)      红色 (2)      绿色通道 (3)      蓝色通道 (4)      RGB通道,也称综合通道 (5)      Alpha通道 (6)      专色通道(只显示金色和银色,用来印刷) 图:   2、  Alpha通道的作用: 得到一个特殊的选取(复杂形状的选取) 3、  滤镜 例子:复杂选框、多边形相框 步骤一

3D图形渲染通道负载优化的几种小技巧

一般来说, 定位渲染通道瓶颈的方法就是改变渲染通道每个步骤的工作量, 如果吞吐量也改变了, 那个步骤就是瓶颈. 找到了瓶颈就要想办法消除瓶颈, 可以减少该步骤的工作量, 增加其他步骤的工作量. 一般在光栅化之前的瓶颈称作”transform bound”, 三角形设置处理后的瓶颈称作”fill bound” 定位瓶颈的办法 1.         改变帧缓冲或者渲染目标(Ren

适配器模式(大话设计模式)C/C++版本

适配器模式 C++ #include <iostream>using namespace std;// 球员class Player{protected:string name;public:Player(string name) : name(name) {}virtual void Attack() = 0;virtual void Defense() = 0;virtual

备忘录模式(大话设计模式)C/C++版本

备忘录模式 C++ #include <iostream>#include <string>using namespace std;// Memento类,备忘录,此处为角色状态存储箱class RoleStateMemento{private:int m_vit; // 生命力int m_atk; // 攻击力int m_def; // 防御力public:RoleState

大话C语言:第25篇 动态库

1 动态库概述         C语言动态库(也称为共享库)是在程序运行时被加载到内存中的库文件,它包含了可由多个程序共享的代码和数据。动态库在编译时不会被直接链接到目标程序中,而是在程序运行时动态加载。这种特性使得动态库具有一些优势,如节省磁盘空间、便于更新和维护、以及支持多个程序同时共享库代码。         动态库通常以.so(在Linux和类Unix系统中)或.dll(在Window

自制调色小工具给图片加滤镜,修改图片红、绿、蓝通道及亮度,修改图片颜色

上篇:   上篇我们给地图添加了锐化、模糊等滤镜,这篇来写一个小工具给图片调色。 调色比锐化等滤镜要简单许多,直接拿到像素值修改即可。不需要用到卷积核。。。(*^▽^*) 核心原理就是图像结构,使用context.getImageData获取图像像素结构。 const imageData = context.getImageData(0, 0, canvas.width, ca

《大话数据结构》最小生成树——Kruskal算法

/*2014-6-24思想:n个节点的图中,只需要找到权值最小且不与现有边集合构成环的(n-1)条边,必成最小生成树。方案:将边的权值进行筛选,每次找到权值最小的边,补充道边集合中即可。难点:如何确保这些边不构成环——对每个边,让其起始节点是祖先,通过洄游寻根,如果祖先相同说明两个节点是“近亲”,会构成闭环:A-B-C-A三角形中:1. A-B边中确定B的祖先和父亲都是A;2. B-C边中,确定C

Linkin大话Java和internet概念

整理电脑,无意中翻到不知道哪里来的文章,觉得里面写的很好,仔细看过一遍后,整理了下贴了出来,其中的好多概念我觉得讲的很透彻。 既然Java不过另一种类型的程序设计语言,为什么还有这么多的人认为它是计算机程序设计的一个里程碑? Java除了可解决传统的程序设计问题以外,还能解决World Wide Web(万维网)上的编程问题。java比较高级的应用有2块:一块是我现在正在玩的j2