goland 学习 之channel
首先要明确channel是什么
channel是一种类型,一种引用类型。声明通道类型的格式如下:
var test chan type
test := make(chan struct{}) (make只能创建 slice,map,chan)
make和new的区别 引用资料 (make 创建一个类型,并初始化,返回引用 new 创建一个类型 并初始化零值,返回内存地址)
指针和引用的区别 引用资料 (指针是引用的一种特例,是一类简单的透明引用,区别于不透明的引用)
接下来要知道channel的作用
goland程序并发执行体是goroutine(G协程)。如果同时有多个goroutine并发执行,然后这时不同goroutine又需要做数据交互时怎么办呢?常规做法是利用共享内存来处理,但是在并发的情况下共享内存的使用会有竟态问题,就需要加解锁操作,这就会造成资源消耗。 所以Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。这样channel就起到了通信管道的作用
channel的分类
有缓冲channel
可以理解为自带一个存储池的管道 ,池满则发送方阻塞,池空就接收方阻塞 (可以利用这个特性做限流降级等功能,常见如令牌桶,漏桶等功能)
无缓冲
就是一根管子,相当于缓存空间为1单位的有缓冲管道。
应用一 循环打印 1234
package main import ( "fmt" "time" ) type token struct{} func channel2() { num := 4 var chs []chan token // 4 个work for i := 0; i < num; i++ { chs = append(chs, make(chan token)) } for j := 0; j < num; j++ { go worker(j, chs[j], chs[(j+1)%num]) } // 先把令牌交给第一个 chs[0] <- struct{}{} select {} } func worker(id int, ch chan token, next chan token) { for { // 对应work 取得令牌 token := <-ch fmt.Println(id + 1) time.Sleep(1 * time.Second) // 传递给下一个 next <- token } } func main() { channel2() }
应用二 有缓冲的channel实现限流
package main import ( "fmt" "time" ) func channel3() { limit := make(chan struct{}, 3) jobCount := 10 start(jobCount, limit) select {} time.Sleep(time.Duration(50) * time.Second) } func start(jobCount int, limit chan struct{}) { for i := 0; i < jobCount; i++ { go func(index int) { limit <- struct{}{} job(index) <-limit }(i) } } func job(index int) { // 耗时任务 time.Sleep(time.Duration(index) * time.Second) fmt.Println("任务:已完成:", index, time.Now().Format("2006-01-02 15:04:05"), "\n") }
应用三 带超时的限流器
package main import "goland/utill" func channel4() { s := utill.CreateLimitServer(300, 3) for i := 0; i < 10; i++ { go s.LimitStart(i) } select {} } package utill import ( "fmt" "time" ) type Server struct { tk *time.Ticker reset chan struct{} Close chan struct{} Limit chan struct{} Period int64 } func CreateServer(Period int64) *Server { return &Server{ tk: nil, reset: make(chan struct{}), Limit: make(chan struct{}), Close: make(chan struct{}), Period: Period, } } func CreateLimitServer(Period int64, limit int) *Server { return &Server{ tk: nil, reset: make(chan struct{}), Limit: make(chan struct{}, limit), Close: make(chan struct{}), Period: Period, } } func (s *Server) Update(p int64) { s.Period = p s.reset <- struct{}{} } func (s *Server) Stop() { //s.Close <- Type close(s.Close) close(s.reset) } // 程序启动 func (s *Server) Start() { // 定时 s.tk = time.NewTicker(time.Duration(s.Period) * time.Second) defer s.tk.Stop() for { select { case <-s.tk.C: fmt.Println("定时唤醒:", time.Now().Format("2006-01-02 15:04:05")) case <-s.reset: fmt.Println("reset:", s.Period, time.Now().Format("2006-01-02 15:04:05")) s.tk.Reset(time.Duration(s.Period) * time.Second) case <-s.Close: fmt.Println("Close:") return } } } func (s *Server) LimitStart(index int) { // 定时 s.Limit <- struct{}{} jobs(s, index) s.tk = time.NewTicker(time.Duration(s.Period) * time.Second) defer s.tk.Stop() for { select { case <-s.tk.C: fmt.Println("定时唤醒:", time.Now().Format("2006-01-02 15:04:05")) case <-s.reset: fmt.Println("reset:", s.Period, time.Now().Format("2006-01-02 15:04:05")) s.tk.Reset(time.Duration(s.Period) * time.Second) case <-s.Close: fmt.Println("Close:") return } } } func jobs(s *Server, index int) { // 耗时任务 time.Sleep(2 * time.Second) fmt.Println("任务:已完成:", index, time.Now().Format("2006-01-02 15:04:05"), "\n") <-s.Limit }
本文来自博客园,作者:给香菜送点香菜,转载请注明原文链接:https://www.cnblogs.com/mingkewang/articles/17159868.html

浙公网安备 33010602011771号