首頁  >  文章  >  後端開發  >  所有 goroutine 都在睡覺 - 死鎖,在緩衝通道上,不明白為什麼

所有 goroutine 都在睡覺 - 死鎖,在緩衝通道上,不明白為什麼

王林
王林轉載
2024-02-08 20:50:13880瀏覽

所有 goroutine 都在睡觉 - 死锁,在缓冲通道上,不明白为什么

問題內容

我只想建立一定數量的 go 例程,例如 5 個,但我可以接收可變數量的作業。

這是我嘗試執行此操作的程式碼,測試位於其下方。

package main

import (
    "context"
    "fmt"
    "runtime"
    "time"
)

func dowork(size int, capacity int) int {
    start := time.now()
    jobs := make(chan *job, capacity)
    results := make(chan *job, capacity)
    sem := make(chan struct{}, capacity)
    go chanworker(jobs, results, sem)
    for i := 0; i < size; i++ {
        jobs <- &job{id: i}
    }
    close(jobs)
    successcount := 0
    for i := 0; i < size; i++ {
        item := <-results
        if item.result {
            successcount++
        }
        fmt.printf("job %d completed %v\n", item.id, item.result)
    }
    close(results)
    close(sem)
    fmt.printf("time taken to execute %d jobs with %d capacity = %v\n", size, capacity, time.since(start))
    return successcount
}

func chanworker(jobs <-chan *job, results chan<- *job, sem chan struct{}) {

    for item := range jobs {
        it := item
        sem <- struct{}{}
        fmt.printf("job %d started\n", it.id)
        go func() {
            timeoutctx, cancel := context.withtimeout(context.background(), 300*time.millisecond)
            defer cancel()
            time.sleep(time.duration(it.id) * 100 * time.millisecond)
            select {
            case <-timeoutctx.done():
                fmt.printf("job %d timed out\n", it.id)
                it.result = false
                results <- it
                <-sem
                return
            default:
                fmt.printf("total number of routines %d\n", runtime.numgoroutine())
                it.result = true
                results <- it
                <-sem
            }
        }()
    }
}


對此的測試

package main

import (
    "testing"
)

func Test_doWork(t *testing.T) {
    type args struct {
        size     int
        capacity int
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        {
            name: "jobs 10 capacity 5",
            args: args{
                size:     10,
                capacity: 5,
            },
            want: 3,
        },
        {
            name: "jobs 100 capacity 5",
            args: args{
                size:     100,
                capacity: 5,
            },
            want: 3,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := doWork(tt.args.size, tt.args.capacity); got < tt.want {
                t.Errorf("doWork() = %v, want %v", got, tt.want)
            }
        })
    }
}

測試 jobs 10 容量 5 有效,但 jobs 100 容量 5 失敗。

如果我為 100 個作業設定容量 50,它可以工作,但對於 30 個作業則不起作用,並且無法理解其行為。

以下是我對頻道的理解並期望它能夠發揮作用。

緩衝通道如果已滿,將會阻塞,直到有一些空閒容量可用。我預計一旦 jobs 通道滿了,它就會阻塞,直到 chanworker 釋放其中的一些。 chanworker 本身接收一個容量並使用空結構來確保建立的工作執行緒不超過 5 個。

為什麼我會遇到錯誤 致命錯誤:所有 goroutine 都在休眠 - 死鎖!


正確答案


由於主goroutine 在所有作業都傳送到jobs 之前不會從results 接收值,因此工作執行緒會在傳送到results 時阻塞。主 goroutine 阻止發送到 jobs 因為工作被阻止。陷入僵局!

透過使用 goroutine 來完成工作來修復。

go func() {
    for i := 0; i < size; i++ {
        jobs <- &Job{id: i}
    }
    close(jobs)
}()

https://www.php.cn/link/6e04df31f1bbb1c02666d0dfa3638f76

以上是所有 goroutine 都在睡覺 - 死鎖,在緩衝通道上,不明白為什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:stackoverflow.com。如有侵權,請聯絡admin@php.cn刪除