介绍

本篇文章给大家分享的是有关利用golang怎么限制同一时间的并发数量,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。

去的并发量是很厉害的,goroutine创建的代价极小,其中一个重要的原因是因为去采用了分段栈技术,每一个goroutine只占极小的空间。与此同时,goroutine是语言层面的,减少了内核态到用户态的切换开销,并且goroutine摒弃了一些golang用不到的一些操作系统线程的系统调用,创建代价小。

我们可以一瞬间创建很多个goroutine,这是相当容易的。

乍一看,这与题目完全不符,前面说了那么多,难道不是鼓励我们多创建goroutine吗?不不不,goroutine确实很好用,但是如果不加以限制,很有可能出现其他的不可预料的错误。

比如在web领域中,一个连接,在linux/unix下就相当于是打开了一个文件,占用一个文件描述符。但是系统会规定文件描述符的上限,我们可以使用ulimit - n来进行查看,如果我们遵循量大就好的话,那么一拥而上的请求连接会瞬间报错。

2018/06/30 10:09:54表盘tcp: 8080:套接字:打开的文件太多

上面这条报错信息源于我写的一个循环请求的工具

package 主要   import  (   “才能sync"   “才能net"   “才能strconv"   “才能fmt"   “才能log"   )   const  (   MAX_CONCURRENCY 才能=,10000,   )   var  waitGroup  sync.WaitGroup   func  main () {   并发性才能()   waitGroup.Wait才能()   }//进行网络io   func 请求(currentCount  int) {   fmt.Println才能(“request", +, strconv.Itoa (currentCount), +,“\ r")   ,,康涅狄格州,err :=, net.Dial (“tcp"“: 8080“)   if 才能;err  !=, nil  {, log.Fatal (err),}   defer 才能;conn.Close ()   defer 才能;waitGroup.Done ()   }//并发请求   func 并发(){   for 才能;小姐::=,0;小姐:& lt;, MAX_CONCURRENCY;我+ +,{   ,,,waitGroup.Add (1)   ,,,go 请求(我)   ,,}   }

用去建立一个服务端很简单,我这里简单的贴下服务器的代码

package 主要   import  (   “才能无力偿贷;   “才能os"   “才能fmt"   “才能net"   )   func  checkErr (err 错误){   if 才能;err  !=, nil  {, fmt.Fprintln (os.Stderr,犯错),}   }   func  main (), {   倾听者,才能,err :=, net.Listen (“tcp"“: 8080“)   checkErr才能(err)   for {才能   ,,,康涅狄格州,err :=, listener.Accept ()   ,,,checkErr (err)   ,,,go  func (conn  net.Conn) {,   ,,,,,,err  _:=, io.WriteString(康涅狄格州,,“欢迎光临!“),   ,,,,,checkErr (err)   ,,,,,defer  conn.Close ()   ,,,}(康涅狄格州)   ,,}   }

现在回到主题,我们可以看到一拥而上其实也有坏处,想要解决这一问题,我们可以限制同一时间的并发数量,可以利用通道来达到这一点,这有点类似于信号量(信号量)

创建一个带缓存的通道,其中CHANNEL_CACHE为同一时间的最大并发量

想简单的说一下为什么这陈里的类型要用一个空的结构,这是因为在这个场景下(限制同一时间的并发量),通过通道传输的数据的类型并不重要,我们只需要通过做一个通知效果就行了(就像你通知你朋友起床,你只用闪个电话,而不用实际的接通,省去了电话费的开销),这里的空的struct实际上是不占任何空间的,因此这里选用空的struct

const  (   CHANNEL_CACHE 才能=200   )   var  tmpChannel =,使(chan 结构{},CHANNEL_CACHE)

在与服务器建立连接的地方这样写(是不是很类似于信号量)

tmpChan  & lt;作用;结构{}{}   康涅狄格州,err :=, net.Dial (“tcp"“: 8080“)   & lt;安康;tmpChan

这样同一时间的并发量就由CHANNEL_CACHE限制下来

经过循环开启的goroutine在请求服务器之前会向通道发送消息,如果缓存满了,那么说明已经有CHANNEL_CACHE个goroutine在进行与服务器的连接,接着就会阻塞在这里,等待其中一个goroutine处理完之后,从通道中读出一个空的结构,这时阻塞的地方向通道发送一个空结构,就可以与服务器建立连接了

下面贴一下全部的代码

package 主要   import  (   “才能sync"   “才能net"   “才能strconv"   “才能fmt"   “才能log"   )   const  (   MAX_CONCURRENCY 才能=,10000,   CHANNEL_CACHE 才能=200   )   var  tmpChan =,使(chan 结构{},MAX_CONCURRENCY)   var  waitGroup  sync.WaitGroup   func  main () {   并发性才能()   waitGroup.Wait才能()   }//进行网络io   func 请求(currentCount  int) {   fmt.Println才能(“request", +, strconv.Itoa (currentCount), +,“\ r")   tmpChan 才能;& lt;作用;结构{}{}   ,,康涅狄格州,err :=, net.Dial (“tcp"“: 8080“)   & lt;作用;tmpChan才能   if 才能;err  !=, nil  {, log.Fatal (err),}   defer 才能;conn.Close ()   defer 才能;waitGroup.Done ()   }//并发   func 并发(){   for 才能;小姐::=,0;小姐:& lt;, MAX_CONCURRENCY;我+ +,{   ,,,waitGroup.Add (1)   ,,,go 请求(我)   }   }