Golang簡單製作一個池

奇蹟師發表於2021-06-15

1.代理模式

2.執行緒生命週期(先看圖) — 瞭解執行緒的生命週期即可

3.go runtine

4.死鎖,以及防止死鎖, sync.Mutex

5.sync.WaitGroup 計數器

  • 為了減少記憶體的重複呼叫
  • 減少上下文切換時間
  • 根據合適的場景運用池,不合適的場景用池反而會適得其反
  • 由於go runtine 中的記憶體排程足夠好,不需要進行記憶體的分配
  • 可以通過golang中限制 Goruntine的併發數,來限制記憶體排程
  • segmentfault.com/a/119000001795639...

1.確定任務和池的引數

const (
    STOPED = iota
    RUNNING
)

// Task 任務
type Task struct {
    //傳入函式
    Handler func(v ...interface{})
    //需要傳入的引數(v)
    Params  []interface{}
}

// Pool 池
type Pool struct {
    //最大容量
    MaxCap int
    //正在進行的任務數量
    active int
    //池的狀態 RUNNING OR STOPED
    status int
    //任務通道
    chTask chan *Task
    //鎖
    mu     sync.Mutex
    //用於等待一組執行緒的結束
    wg     sync.WaitGroup
}

2.根據代理模式和執行緒的生命週期建立函式

// NewPool 建立
func NewPool(maxCap int) (*Pool, error) {
    return nil,nil
}


// Put 就緒
func (p *Pool) Put(task *Task) error {
    return nil
}

// run 執行
func (p *Pool) run() {

}

//worker 阻塞
func (p *Pool) worker() {

}

// Close 結束
func (p *Pool) Close() {
}

3.填充內容

1.建立

// NewPool 建立
// 1.判斷容量是否合理
// 2.初始化池
func NewPool(maxCap int) (*Pool, error) {
    if maxCap <= 0 {
        return nil, errors.New("invalid pool cap")
    }

    return &Pool{
        MaxCap: maxCap,
        status: RUNNING,
        chTask: make(chan *Task, maxCap),
    }, nil
}

2.就緒

// Put 就緒
// 1.加鎖 --- 破壞迴圈等待條件
// 2.判斷池的狀態
// 3.放入訊息佇列
// 4.計數器+1
// 5.轉到執行態
func (p *Pool) Put(task *Task) error {
    p.mu.Lock()
    defer p.mu.Unlock()

    if p.status == STOPED {
        return ErrPoolAlreadyClosed
    }

    //放入訊息佇列中
    p.chTask <- task
    //計數器+1
    p.wg.Add(1)

    //執行執行緒
    if p.active < p.MaxCap {
        p.run()
    }
    return nil
}

3.執行

// run 執行
// 1.執行數+1
// 2.通過 go runtine 執行
func (p *Pool) run() {
    p.active++
    go p.worker()
}

4.阻塞

//worker 阻塞
// 1.消費通道中的任務 <-p.chTask
//   select在channel為nil時會阻塞佇列
// 2.執行函式
// 3.活動數-1
// 4.減少WaitGroup計數器的值
func (p *Pool) worker() {
    //延後執行
    defer func() {
        p.active--
        p.wg.Done()
    }()

    for {
        select {
        case task, ok := <-p.chTask:
            if !ok {
                return
            }
            task.Handler(task.Params...)
        }
    }
}

5.關閉

// Close 結束
// 1.設定狀態為結束
// 2.關閉執行緒
// 3.阻塞直到WaitGroup計數器減為0
func (p *Pool) Close() {
    p.status = STOPED

    close(p.chTask)

    p.wg.Wait()
}

4.測試

func TestPool(t *testing.T) {
    t.Run("pool", func(t *testing.T) {

        pool, err := NewPool(20)
        defer pool.Close()
        assert.Nil(t, err)

        for i := 0; i < 20; i++ {
            pool.Put(&Task{
                Handler: func(v ...interface{}) {
                    fmt.Println(v)
                },
                Params: []interface{}{i},
            })
        }
    })
}

segmentfault.com/a/119000002146835...

www.cnblogs.com/wongbingming/p/130...

geektutu.com/post/hpg-concurrency-...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章