Golang Channel的一些妙用

2024-08-23 20:58
文章标签 golang channel 妙用

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

Golang中通过我们使用Channel来传递信息、信号,经典的如生产者消费者、退出信号等, 那么除此之外Channel还有哪些不常见的用法。

限制并发数

Golang原生提供了强大的并发原语,但如果无节制的使用大量Goroutine,并发过大会造成资源浪费,严重时会导致程序崩溃。使用带缓冲区的Channel可以解决此类问题。

在Golang的godoc/gatevfs中实现了对最大虚拟文件的并发限制。

// New returns a new FileSystem that delegates to fs.
// If gateCh is non-nil and buffered, it's used as a gate
// to limit concurrency on calls to fs.
func New(fs vfs.FileSystem, gateCh chan bool) vfs.FileSystem {if cap(gateCh) == 0 {return fs}return gatefs{fs, gate(gateCh)}
}type gate chan boolfunc (g gate) enter() { g <- true }
func (g gate) leave() { <-g }

通过带缓存的Channel,每次打开文件时调用enter发生数据到Channel,当文件关闭时调用leave读取Channel数据,当前Channel满后再次读取变会阻塞,直到有资源被释放,从而达到限制并发数的目的。

func (fs gatefs) Open(p string) (vfs.ReadSeekCloser, error) {fs.enter()defer fs.leave()rsc, err := fs.fs.Open(p)if err != nil {return nil, err}return gatef{rsc, fs.gate}, nil
}

可以配合WaitGroup来实现最大并发数的控制,具体代码如下:

// control number
type Gocontrol struct {ch chan struct{}wg sync.WaitGroup
}func NewGocontrol(number int) *Gocontrol {return &Gocontrol{ch: make(chan struct{}, number),}
}func (g *Gocontrol) Enter() {g.ch <- struct{}{}
}func (g *Gocontrol) Leave() {<-g.ch
}func (g *Gocontrol) Run(f func()) {g.Enter()g.wg.Add(1)go func() {defer g.Leave()defer g.wg.Done()f()}()
}func (g *Gocontrol) Wait() {g.wg.Wait()
}

测试运行,创建100个任务,调用Gocontrol限制最大并发为10,运行runtime.NumGoroutine来获取当前Goroutine数

func RunGocontrol() {go func() {for {fmt.Printf("goroutine numbers: %v\n", runtime.NumGoroutine())time.Sleep(500 * time.Millisecond)}}()gctl := NewGocontrol(10)start := time.Now()for i := 0; i < 100; i++ {n := igctl.Run(func() {time.Sleep(1 * time.Second)fmt.Println("hello", n)})}gctl.Wait()dur := time.Since(start)fmt.Printf("run time: %v", dur)
}

运行结果显示,最大Goroutine数为12(包含1个主线程,1个监控线程,10个任务线程),符合预期

goroutine numbers: 12
run time: 10.002604769s

实现锁

除了调用sync包,使用Channel也可以实现锁,以互斥锁为例:

type ChLock chan struct{}func NewChLock() ChLock {return make(chan struct{}, 1)
}func (l ChLock) Lock() {l <- struct{}{}
}func (l ChLock) Unlock() {<-l
}

互斥锁通过容量为1的Channel实现互斥,同样借助多个Channel可以使用读写锁,通过关闭Channel可以实现类型Once的功能。

从源码层面分析,Channel其实是一个线程安全的环形队列,Channel定义在runtime/chan.go中:

type hchan struct {qcount   uint           // total data in the queuedataqsiz uint           // size of the circular queuebuf      unsafe.Pointer // points to an array of dataqsiz elementselemsize uint16closed   uint32elemtype *_type // element typesendx    uint   // send indexrecvx    uint   // receive indexrecvq    waitq  // list of recv waiterssendq    waitq  // list of send waiterslock mutex
}

其中包含了lock锁结构,这里的mutex不同于sync.Mutex,只在Runtime内部使用是一种低阶的同步原语,也没有提供Lock/Unlock方法,只能通过全局的lock/unlock/initlock等函数调用。

最后

本文介绍了一些Channel的妙用,限制并发数与实现锁等,通过示例及源码阐述其深层次的原因。

Explore more in https://qingwave.github.io

这篇关于Golang Channel的一些妙用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

poj 3050 dfs + set的妙用

题意: 给一个5x5的矩阵,求由多少个由连续6个元素组成的不一样的字符的个数。 解析: dfs + set去重搞定。 代码: #include <iostream>#include <cstdio>#include <set>#include <cstdlib>#include <algorithm>#include <cstring>#include <cm

Go Channel的实现

channel作为goroutine间通信和同步的重要途径,是Go runtime层实现CSP并发模型重要的成员。在不理解底层实现时,经常在使用中对channe相关语法的表现感到疑惑,尤其是select case的行为。因此在了解channel的应用前先看一眼channel的实现。 Channel内存布局 channel是go的内置类型,它可以被存储到变量中,可以作为函数的参数或返回值,它在r

Golang进程权限调度包runtime

关于 runtime 包几个方法: Gosched:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行GOMAXPROCS:设置最大的可同时使用的 CPU 核数Goexit:退出当前 goroutine(但是defer语句会照常执行)NumGoroutine:返回正在执行和排队的任务总数GOOS:目标操作系统NumCPU:返回当前系统的 CPU 核数量 p

Golang 网络爬虫框架gocolly/colly(五)

gcocolly+goquery可以非常好地抓取HTML页面中的数据,但碰到页面是由Javascript动态生成时,用goquery就显得捉襟见肘了。解决方法有很多种: 一,最笨拙但有效的方法是字符串处理,go语言string底层对应字节数组,复制任何长度的字符串的开销都很低廉,搜索性能比较高; 二,利用正则表达式,要提取的数据往往有明显的特征,所以正则表达式写起来比较简单,不必非常严谨; 三,使

Golang网络爬虫框架gocolly/colly(四)

爬虫靠演技,表演得越像浏览器,抓取数据越容易,这是我多年爬虫经验的感悟。回顾下个人的爬虫经历,共分三个阶段:第一阶段,09年左右开始接触爬虫,那时由于项目需要,要访问各大国际社交网站,Facebook,myspace,filcker,youtube等等,国际上叫得上名字的社交网站都爬过,大部分网站提供restful api,有些功能没有api,就只能用http抓包工具分析协议,自己爬;国内的优酷、

Golang网络爬虫框架gocolly/colly(三)

熟悉了《Golang 网络爬虫框架gocolly/colly 一》和《Golang 网络爬虫框架gocolly/colly 二》之后就可以在网络上爬取大部分数据了。本文接下来将爬取中证指数有限公司提供的行业市盈率。(http://www.csindex.com.cn/zh-CN/downloads/industry-price-earnings-ratio) 定义数据结构体: type Zhj

Golang支持平滑升级的HTTP服务

前段时间用Golang在做一个HTTP的接口,因编译型语言的特性,修改了代码需要重新编译可执行文件,关闭正在运行的老程序,并启动新程序。对于访问量较大的面向用户的产品,关闭、重启的过程中势必会出现无法访问的情况,从而影响用户体验。 使用Golang的系统包开发HTTP服务,是无法支持平滑升级(优雅重启)的,本文将探讨如何解决该问题。 一、平滑升级(优雅重启)的一般思路 一般情况下,要实现平滑

Golang服务平滑重启

与重载配置相同的是我们也需要通过信号来通知server重启,但关键在于平滑重启,如果只是简单的重启,只需要kill掉,然后再拉起即可。平滑重启意味着server升级的时候可以不用停止业务。 我们先来看下Github上有没有相应的库解决这个问题,然后找到了如下三个库: facebookgo/grace - Graceful restart & zero downtime deploy for G

Golang test编译使用

创建文件my_test.go package testsimport "testing"func TestMy(t *testing.T) {t.Log("TestMy")} 通常用法: $ go test -v -run TestMy my_test.go=== RUN TestMyTestMy: my_test.go:6: TestMy--- PASS: TestMy (0.

Golang GUI入门——andlabs ui

官方不提供gui标准库,只好寻求第三方库。 https://github.com/google/gxui 这个gui库是谷歌内部人员提供的,并不是谷歌官方出品,现在停止维护,只好作罢。 第三方gui库 找了好多,也比较了好多,最终决定使用的是还是 https://github.com/andlabs/ui 相信golang gui还会发展的更好,期待更优秀的gui库 由于andlabs