亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何實現 Go 并發 map 或 slice 以更快地管理使用中的資源?

如何實現 Go 并發 map 或 slice 以更快地管理使用中的資源?

Go
慕虎7371278 2022-10-10 15:30:14
圖像您有一個結構,該結構表示一次只有一個用戶可以訪問的資源??赡芸雌饋硐襁@樣: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我會鎖定以避免任何系統級鎖定(flock)并在 Go 中完成這一切還需要有一個功能來標記資源不再使用現在,當我遍歷整個切片時,我在管理器中使用互斥鎖來鎖定訪問。這是安全的,但我希望能夠通過同時搜索已使用的資源并處理兩個嘗試將同一資源標記為 InUse 的 goroutine 來加快速度。更新我特別想知道是否將 ResourceInUse字段設置為 anint64然后 usingatomic.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")}任何可以更好地測試這一點的單元測試也將不勝感激。
查看完整描述

2 回答

?
夢里花落0921

TA貢獻1772條經驗 獲得超6個贊

問題中的GetUnusedResouce函數可能會對所有資源執行比較和交換操作。根據資源數量和應用程序訪問模式,執行受互斥體保護的少量操作會更快。


使用單鏈表實現快速獲取和放置操作。


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 操場上運行測試。


查看完整回答
反對 回復 2022-10-10
?
千萬里不及你

TA貢獻1784條經驗 獲得超9個贊

給定資源是否在使用中不是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()

}


查看完整回答
反對 回復 2022-10-10
  • 2 回答
  • 0 關注
  • 103 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號