Go语言的Channel文章,整个人都感觉不好了

news/2024/7/2 23:11:18

链客,专为开发者而生,有问必答!

此文章来自区块链技术社区,未经允许拒绝转载。

在这里插入图片描述

Go的Channel是一个很强大的并发数据模型,在一个发送者和多个消费者情况下工作得最好,但是如果是多个发送者,那么在Channel关闭时需要协调多个发送者,等待它们发送消费完毕,同时也会导致一个Channel多次关闭的情况,这个问题Go社区已经注意到,并正在试图解决:issue#14601

让我们看看来自

go channels are bad and you should feel bad | jtol一文是如何抱怨Channel有多不好,以下是原文大意转述:

首先,不用质疑的是,该文博主自从2010中期就开始使用Go语言,参与编写了425K行的纯Go代码,并为Go社区提供了不少开源项目。

Go语言是以Channel和Goroutine(轻量线程)著称,它的理论模型主要是基于CSP模型(Communicating Sequential Processes),但是该文博主认为,从程序员角度看,Go的Channel模型实现方式是错误的,它是一个彻底的反模式。

CSP模型是一种高并发计算模型,这种模型本身是试图通过一个通道同步管理发送和接受,但是,如果一旦你引入其他同步机制如互斥锁mutex、semaphore或条件判断变量,你就再也不是纯的CSP了。

下面让我们看看使用GO编写CSP模型,这个案例是关于如何接受到选手的最高分。

首先,使用Game作为数据结构:

type Game struct {
bestScore int
scores chan int
}

这里状态变量bestScore 并不使用互斥锁进行保护,因为我们只有一个goroutine管理这个状态,新的分数是通过一个channel接受的。

func (g *Game) run() {
for score := range g.scores {
if g.bestScore < score {
g.bestScore = score
}
}
}

现在开始一个启动game的构造器:

func NewGame() (g *Game) {
g = &Game{
bestScore: 0,
scores: make(chan int),
}
go g.run()
return g
}

下一步,让我们假设有人给我们一个Player接口,这个接口可以获得分数score,也可能返回错误,因为也许网络连接错误或player已经退出。

type Player interface {
NextScore() (score int, err error)
}

下面是处理player,首先检查错误,然后将接受到的分数发给channel。

func (g *Game) HandlePlayer(p Player) error {
for {
score, err := p.NextScore()
if err != nil {
return err
}
g.scores <- score
}
}

好了,我们有一个Game类型,能够以线程安全方式跟踪Player的最高分。

当你将这个游戏上线运行后,你获得了非常地成功,你的游戏服务器上运行了很多这样的Game程序。但是你会发现人们有时会离开你的游戏,许多游戏里面没有任何玩家,但是游戏本身没有停止而是不停地在循环运行,你被(*Game).run协程搞得不知所措了。

其实这个案例完全可以不使用Channel简单地完成:

type Game struct {
mtx sync.Mutex
bestScore int
}

func NewGame() *Game {
return &Game{}
}

func (g *Game) HandlePlayer(p Player) error {
for {
score, err := p.NextScore()
if err != nil {
return err
}
g.mtx.Lock()
if g.bestScore < score {
g.bestScore = score
}
g.mtx.Unlock()
}
}

这里只是引入了互斥锁,替代了原来的Channel方式。使用传统的同步锁替代CSP的Go channel居然解决了问题。

那么,让我们看看Go语言的Channel背后是什么?Channel是使用锁保证连续访问的线程安全性,使用Channel同步化内存的访问,其实你是在使用锁(banq注:这违背了CSP模型本身定义,CSP存在的前提就是避免使用锁。),锁被线程安全的队列给包装了,那么Go的这个神秘的锁与直接使用标准库中mutex有什么区别?下面是两者性能测试结果:

BenchmarkSimpleSet-8 3000000 391 ns/op
BenchmarkSimpleChannelSet-8 1000000 1699 ns/op

对于unbuffered channel也是同样测试结果。(测试结果表明:Channel性能比mutex性能要差得多)

也许Go的调度器会提高,但是这意味着老的旧的互斥锁和条件变量表现得非常好,更有效,更快速。如果你需要性能,那么就使用这些真正的方法(互斥锁等方法)。

此后,该文作者还指出Channel其实并不能和其他并发原始模型很好地组合在一起。比如Channel和互斥锁放在一起就带来不确定性。

他还指出,在API中使用Channel反而使得问题变得糟糕,你完全可以在没有任何协程情况下使用互斥锁mutexe, semaphores,和回调callbacks编写API,有时,回调函数反而更加强大,虽然,goroutine协程口口声声说是用来替代万恶的回调函数的。协程只是比线程较为轻量,但是较为轻量不代表它是最轻量的。

他还谈到:对一个已经关闭的Channel再进行关闭或发送操作是非常痛苦,如果你要关闭一个Channel,需要将关闭状态进行同步化操作,使用mutex互斥锁等,但是它们又不能很好地协调在一起,为什需要有关闭的状态?因为这样才会防止其他写入者同时写入或关闭一个已经关闭的channel。


http://lihuaxi.xjx100.cn/news/236938.html

相关文章

建立CentOS 6.9 的Yum本地源

1、建立一个本地Yum的软件仓库1mkdir /media/cdrom2、把CentOS6.9光盘装载到/media/cdrom1mount /dev/cdrom /media/cdrom3、安装createrepo1 rpm -ivh /media/cdrom/Packages/createrepo-[按tab键] deltarpm-[按tab键] python-deltarpm-[按tab键] createrepo-0.9.9-26.…

iOS开发之AVKit框架使用

2019独角兽企业重金招聘Python工程师标准>>> iOS开发之AVKit框架使用 一、引言 在iOS开发框架中&#xff0c;AVKit是一个非常上层&#xff0c;偏应用的框架&#xff0c;它是基于AVFoundation的一层视图层封装。其中相关文件和类都十分简单&#xff0c;本篇博客主要整…

Go语言开发常见陷阱,你遇到过几个?

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 Go作为一种简便灵巧的语言&#xff0c;深受开发者的喜爱。但对于初学者来说&#xff0c;要想轻松驾驭它&#xff0c;还得做好细节学习工作。 初学者…

【数据结构】Java实现数据结构的前置知识,时间复杂度空间复杂度,泛型类的讲解

文章目录数据结构时间复杂度、空间复杂度包装类、装箱与拆箱泛型擦除机制数据结构 当我们在成为一名程序员的这条道路上努力的时候&#xff0c;我们一定经常听到这个词数据结构。那么究竟什么是数据结构呢&#xff1f;数据结构顾名思义&#xff0c;就是数据结构&#xff0c;数…

matlab处理txt文件数据

read_txtfile.,m clear close all clc %load函数一般将用来导入纯数字的文件&#xff0c;可以是文本格式的文件或者是matlab保存的mat格式的文件 positionload(坐标点.txt); %将.txt数据读入到matlab工作空间[m,n]size(position); %获得数据矩阵的大小 j1; sumx0; sumy0; …

ORACLE中通过DBMS_CRYPTO包对表敏感字段进行加密

http://doc.primeton.com/pages/viewpage.action?pageId4917998

区块链之智能合约详解

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 什么是智能合约&#xff1f; 智能合约又称智能合同&#xff0c;是由事件驱动的、具有状态的、获得多方承认的、运行在区块链之上的、且能够根据预设…

文件操作示例脚本 tcl

linux 下&#xff0c;经常会对用到文件操作&#xff0c;下面是一个用 tcl 写的文件操作示例脚本&#xff1a; 其中 set f01 [open "fix.tcl" w] 命令表示 打开或者新建一个文件“fix.tcl”&#xff0c;并将其 file ID 设置为 f01&#xff0c;后续就以这个 file ID 来…