如圖所示,您有一個結構體,表示一次只有一個用戶可以訪問的資源。可能看起來像這樣:
type Resource struct{
InUse bool//or int32/int64 is you want to use atomics
Resource string //parameters that map to the resource, think `/dev/chardeviceXXX`
}
這些資源的數量是有限的,用戶會隨機并發地請求訪問它們,因此您將它們打包在管理器中
type ResourceManager struct{
Resources []*Resource //or a map
}
我試圖找出經理創建一個函式的最佳、安全的方法,該函式func (m *ResourceManager)GetUnusedResouce()(*Resouce,error)將:
- 迭代所有資源,直到找到一個不是 InUse 的資源
- 將其標記為 InUse 并將 *Resouce 回傳給呼叫背景關系/goroutine
- 我會鎖定以避免任何系統級鎖定(群)并在 Go 中完成所有這些
- 還需要有一個功能來標記資源不再使用
現在,當我遍歷整個切片時,我在管理器中使用互斥鎖來鎖定訪問。這是安全的,但我希望通過能夠同時搜索已使用的資源并處理兩個試圖將同一資源標記為 InUse 的 goroutine 來加快速度。
更新
我特別想知道是否將 ResourceInUse欄位設定為 anint64然后使用atomic.CompareAndSwapInt64會允許資源管理器在找到未使用的資源時立即鎖定:
func (m *ResourceManager)GetUnusedResouce()(*Resouce,error){
for i := range Resources{
if atomic.CompareAndSwapInt64(&Resouces[i].InUse,1){
return Resouces[i],nil
}
}
return nil, errors.New("all resouces in use")
}
任何可以更好地測驗這一點的單元測驗也將不勝感激。
uj5u.com熱心網友回復:
問題中的GetUnusedResouce函式可能會對所有資源執行比較和交換操作。根據資源數量和應用程式訪問模式,執行受互斥鎖保護的少量操作可能會更快。
使用單向鏈表來實作快速的 get 和 put 操作。
type Resource struct {
next *Resource
Resource string
}
type ResourceManager struct {
free *Resource
mu sync.Mutex
}
// Get gets a free resource from the manager or returns
// nil when the manager is empty.
func (m *ResourceManager) Get() *Resource {
m.mu.Lock()
defer m.mu.Unlock()
result := m.free
if m.free != nil {
m.free = m.free.next
}
return result
}
// Put returns a resource to the pool.
func (m *ResourceManager) Put(r *Resource) {
m.mu.Lock()
defer m.mu.Unlock()
r.next = m.free
m.free = r
}
這是在測驗中使用的示例:
func TestResourceManager(t *testing.T) {
// Add free resources to a manager.
var m ResourceManager
m.Put(&Resource{Resource: "/dev/a"})
m.Put(&Resource{Resource: "/dev/b"})
// Test that we can get all resources from the pool.
ra := m.Get()
rb := m.Get()
if ra.Resource > rb.Resource {
// Sort ra, rb to make test independent of order.
ra, rb = rb, ra
}
if ra == nil || ra.Resource != "/dev/a" {
t.Errorf("ra is %v, want /dev/a", ra)
}
if rb == nil || rb.Resource != "/dev/b" {
t.Errorf("rb is %v, want /dev/b", rb)
}
// Check for empty pool.
r := m.Get()
if r != nil {
t.Errorf("r is %v, want nil", r)
}
// Return one resource and try again.
m.Put(ra)
ra = m.Get()
if ra == nil || ra.Resource != "/dev/a" {
t.Errorf("ra is %v, want /dev/a", ra)
}
r = m.Get()
if r != nil {
t.Errorf("r is %v, want nil", r)
}
}
在操場上運行測驗。
如果資源數量有已知的合理界限,則使用通道。這種方法利用了運行時高度優化的通道實作。
type Resource struct {
Resource string
}
type ResourceManager struct {
free chan *Resource
}
// Get gets a free resource from the manager or returns
// nil when the manager is empty.
func (m *ResourceManager) Get() *Resource {
select {
case r := <-m.free:
return r
default:
return nil
}
}
// Put returns a resource to the pool.
func (m *ResourceManager) Put(r *Resource) {
m.free <- r
}
// NewResourceManager returns a manager that can hold up to
// n free resources.
func NewResourceManager(n int) *ResourceManager {
return &ResourceManager{free: make(chan *Resource, n)}
}
使用TestResourceManager上面的函式測驗這個實作,但替換var m ResourceManager為m := NewResourceManager(4).
在 Go playground 上運行測驗。
uj5u.com熱心網友回復:
給定資源是否在使用中不是Resource它本身的屬性,而是ResourceManager.
事實上,沒有必要跟蹤正在使用的資源(除非由于某些問題中未提及的原因而需要)。使用中的資源可以在釋放時簡單地放回池中。
這是使用通道的可能實作。不需要單個互斥鎖,也不需要任何原子 CAS。
package main
import (
fmt "fmt"
"time"
)
type Resource struct {
Data string
}
type ResourceManager struct {
resources []*Resource
closeCh chan struct{}
acquireCh chan *Resource
releaseCh chan *Resource
}
func NewResourceManager() *ResourceManager {
r := &ResourceManager{
closeCh: make(chan struct{}),
acquireCh: make(chan *Resource),
releaseCh: make(chan *Resource),
}
go r.run()
return r
}
func (r *ResourceManager) run() {
defer close(r.acquireCh)
for {
if len(r.resources) > 0 {
select {
case r.acquireCh <- r.resources[len(r.resources)-1]:
r.resources = r.resources[:len(r.resources)-1]
case res := <-r.releaseCh:
r.resources = append(r.resources, res)
case <-r.closeCh:
return
}
} else {
select {
case res := <-r.releaseCh:
r.resources = append(r.resources, res)
case <-r.closeCh:
return
}
}
}
}
func (r *ResourceManager) AcquireResource() *Resource {
return <-r.acquireCh
}
func (r *ResourceManager) ReleaseResource(res *Resource) {
r.releaseCh <- res
}
func (r *ResourceManager) Close() {
close(r.closeCh)
}
// small demo below ...
func test(id int, r *ResourceManager) {
for {
res := r.AcquireResource()
fmt.Printf("test %d: %s\n", id, res.Data)
time.Sleep(time.Millisecond)
r.ReleaseResource(res)
}
}
func main() {
r := NewResourceManager()
r.ReleaseResource(&Resource{"Resource A"}) // initial setup
r.ReleaseResource(&Resource{"Resource B"}) // initial setup
go test(1, r)
go test(2, r)
go test(3, r) // 3 consumers, but only 2 resources ...
time.Sleep(time.Second)
r.Close()
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/334905.html
上一篇:遞回遍歷任意數量的嵌套映射
