Остановка горутины

Всем привет. Стоит задача: Реализовать остановку горутины. Я делаю так:

package main

import (
    "log"
    "sync"
)

func Work(data []int, lendata int, wg *sync.WaitGroup) {
    quite := make(chan bool)
    for i := 0; i < lendata; i++ {
        go func(i int) {
            log.Println(data[i])
            wg.Done()
            select {
            case <-quite:
                return
            default:
            }
        }(i)
    }
    log.Println("EXIT")
    quite <- true
}

func main() {
    slc := make([]int, 0)
    slc = append(slc, 12, 32, 65, 34, 54, 87)
    wg := sync.WaitGroup{}
    lendata := len(slc)
    wg.Add(lendata)
    Work(slc, lendata, &wg)
    wg.Wait()
}

В выводе я получаю

2022/05/14 14:21:18 EXIT
2022/05/14 14:21:18 32
2022/05/14 14:21:18 12
2022/05/14 14:21:18 34
2022/05/14 14:21:18 65
2022/05/14 14:21:18 54
2022/05/14 14:21:18 87
//порядок естесстенно разный у чисел

В моем понимании, после первого прохода цикла горутина должна остановиться и остальные проходы не выполниться или я не прав?

package main

import (
    "log"
    "sync"
)
//wg.Done() вызывает deadlock и панику, куда бы я его не поставил
//поэтому в этом примере я его не ставлю, так как возможно проблема уже здесь
func Work(data []int, wg *sync.WaitGroup) {
    quite := make(chan bool)
    go func() {
        for _, item := range data {
            log.Println(item)
            select {
            case <-quite:
                return
            default:
            }
        }
    }()
    log.Println("EXIT")
    quite <- true
}

func makechan() {
    slc := make([]int, 0)
    slc = append(slc, 12, 32, 65, 34, 54, 87)
    wg := sync.WaitGroup{}
    lendata := len(slc)
    wg.Add(lendata)
    Work(slc, &wg)
    wg.Wait()
}

Ожидаемое поведение: после первого прохода цикла горутина останавливается не вызывая панику по deadlock. Моя догадка6 не нужно использовать waitgroup

Я сделал такой пример, но он зацикливается на пустом default в Work()


Ответы (1 шт):

Автор решения: Maksim Fedorov

Во-первых: вам не подойдет канал для закрытия НЕСКОЛЬКИХ горутин с 1 сообщением, тк горутин больше чем 1 (разве чт опроверять не читает ли горутина закрытый канал). Другие горутины слушают канал, но от туда ничего не придет — первая горутина вычитала сообщение из канала (то есть забрала его), а в канал никто ничего больше не шлет и др горутины не дождутся сообщения

Во-вторых: Если используете select/case только для прослушка сигнала о закрытии, то нужно обернуть в бесконечный цикл for, чтобы она не была припаркована, тк выполнится default у вас и все — дальше выполняться нечему. А если сейчас for добавить и wg.Done() перед return — программа не завершится (тк горутины не завершатся, тк завершится только первая из них (прошлый пункт))

В-третьих (итог): лучше использовать контекст, вот ответ на ваш вопрос от меня с примером Потoкобезопасное завершение программы

→ Ссылка