Поскольку в Go нет универсальных шаблонов, все готовые решения используют приведение типов, что мне не очень нравится. Я также хочу реализовать это самостоятельно и попробовал следующий код. Однако иногда он не дожидается завершения всех горутин, закрываю ли я канал заданий раньше времени? Мне нечего от них брать. Я мог бы также использовать псевдоканал вывода и ждать, чтобы получить от них точную сумму, но я считаю, что следующий код тоже должен работать. Что мне не хватает?

func jobWorker(id int, jobs <-chan string, wg sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()

    for job := range jobs {
        item := ParseItem(job)
        item.SaveItem()
        MarkJobCompleted(item.ID)
        log.Println("Saved", item.Title)
    }
}

// ProcessJobs processes the jobs from the list and deletes them
func ProcessJobs() {

    jobs := make(chan string)

    list := GetJobs()
    // Start workers
    var wg sync.WaitGroup
    for w := 0; w < 10; w++ {
        go jobWorker(w, jobs, wg)
    }

    for _, url := range list {
        jobs <- url
    }

    close(jobs)
    wg.Wait()
}
0
Mustafa 22 Фев 2016 в 02:35

2 ответа

Лучший ответ

Вызовите wg.Add вне горутины и передайте указатель на группу ожидания.

Если Add вызывается изнутри горутины, основная горутина может вызвать Wait до того, как горутины смогут запустить. Если функция Add не была вызвана, функция Wait немедленно вернется.

Передайте указатель на горутину. В противном случае горутины используют свою собственную копию группы ожидания.

func jobWorker(id int, jobs <-chan string, wg *sync.WaitGroup) {

    defer wg.Done()

    for job := range jobs {
        item := ParseItem(job)
        item.SaveItem()
        MarkJobCompleted(item.ID)
        log.Println("Saved", item.Title)
    }
}

// ProcessJobs processes the jobs from the list and deletes them
func ProcessJobs() {

    jobs := make(chan string)

    list := GetJobs()
    // Start workers
    var wg sync.WaitGroup
    for w := 0; w < 10; w++ {
        wg.Add(1)
        go jobWorker(w, jobs, &wg)
    }

    for _, url := range list {
        jobs <- url
    }

    close(jobs)
    wg.Wait()
}
1
Cerise Limón 21 Фев 2016 в 23:45

Вам нужно передать указатель на группу ожидания, иначе каждое задание получит свою копию.

func jobWorker(id int, jobs <-chan string, wg *sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()

    for job := range jobs {
        item := ParseItem(job)
        item.SaveItem()
        MarkJobCompleted(item.ID)
        log.Println("Saved", item.Title)
    }
}

// ProcessJobs processes the jobs from the list and deletes them
func ProcessJobs() {

    jobs := make(chan string)

    list := GetJobs()
    // Start workers
    var wg sync.WaitGroup
    for w := 0; w < 10; w++ {
        go jobWorker(w, jobs, &wg)
    }

    for _, url := range list {
        jobs <- url
    }

    close(jobs)
    wg.Wait()
}

Посмотрите разницу здесь: без указателя, с указателем.

1
fl0cke 21 Фев 2016 в 23:46