這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。 在上一篇博文《利用緩衝通道來實現網遊帳號驗證訊息的分發和等待》中提到提到利用緩衝通道來實現資料包的分發和等待,並給出了一個原型實現。但是其中的緩衝通道有一個不足,即只能允許一定數量的goroutine在同時使用
SendAndReceive函數等待訊息的分發;如果有更多的goroutine需要等待訊息,則必須等其他goroutine獲得訊息並釋放通道以後自己才能發送資料包並等待回應。這個不足在高並發時限制了系統的輸送量。 為瞭解決這個問題,本文提供一種自適應的通道分配器的實現作為一種解決方案。因為通道也可以換成別的資源,所以認為它本質上是一種資源分派器。這個自適應資源分派器的原理是: 1.預先分配一定數量的資源放到緩衝通道(
緩衝池)裡,以便在申請資源時能夠快速獲得資源 2.如果申請資源時緩衝池為空白,則動態分配資源 3.回收資源時,直接把資源放入緩衝池;如果緩衝池滿則放入一個
備用緩衝池,備用緩衝池是一個數組,所以把資源放入備用緩衝池的動作可以立即完成而不會無限等待。 4.資源分派器自身有一個goroutine在運行,如果檢測到備用緩衝池有資源,則把資源移入緩衝池。 5.當並發數越來越高時,預分配用的資源(即緩衝池和備用緩衝池中的資源)也會越來越多,從而實現了某種程度上的自適應性。 6.因為使用了通道作為通訊手段,因此申請資源和釋放資源的操作都是支援並發的。
代碼實現了一個資源分派器
Pool,資源分派器使用
NewPool函數建立,資源的申請和釋放可以分別通過
Alloc和
Free函數完成。具體代碼如下所示:
// 這裡的資源是chan []byte,因此緩衝池的類型就是chan chan []bytetype Pool struct { chch chan chan []byte back chan chan []byte exit chan bool}func NewPool(count int) *Pool { p := new(Pool) p.back = make(chan chan []byte, count) p.chch = make(chan chan []byte, count) for i := 0; i < count; i++ { p.chch <- make(chan []byte, 1) } p.exit = make(chan bool) go p.run() return p}func (p *Pool) Alloc() chan []byte { select { case ch := <-p.chch: return ch default: break } return make(chan []byte, 1)}func (p *Pool) Free(ch chan []byte) { select { case p.chch <- ch: return default: p.back <- ch }}func (p *Pool) Close() { if p.exit != nil { close(p.exit) p.exit = nil }}func (p *Pool) run() { var chs []chan []byte var chch chan chan []byte var next chan []byte for { select { case <-p.exit: return case ch := <-p.back: if chch == nil { chch = p.chch next = ch } else { chs = append(chs, ch) } case chch <- next: if len(chs) == 0 { chch = nil next = nil } else { next = chs[len(chs)-1] chs = chs[:len(chs)-1] } } }}