@seocoder

Как правильно сделать простой многопоток в golang?

Есть простая задача. Запустить go приложение в N потоков. Данные для потоков есть перед запуском потоков и добавляются в процессе выполнения.

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    ok := make(chan int, 3)
    for i := 0; i < 2; i++ {
        wg.Add(1)

        go func(i int, wg *sync.WaitGroup) {
            for x := range ok {
                fmt.Println(i, x)
                if x >= 5 {
                    ok <- x - 1
                }
            }
            wg.Done()
        }(i, &wg)
    }

    ok <- 1
    ok <- 3
    ok <- 2
    ok <- 5
    ok <- 3
    ok <- 9

    wg.Wait()
}


Не понимаю как правильно "подкитывать" в channel, чтобы не падало все в конце с fatal error: all goroutines are asleep - deadlock!
Чтобы этот код не упал, нужно закрыть channel и туда уже не добавлять. Но мне нужны еще задачи, которые надо выполнить. Как правильно это организовать?!
  • Вопрос задан
  • 440 просмотров
Пригласить эксперта
Ответы на вопрос 3
@ghostiam
На Go писатель, серверов пинатель.
Вы блокируете основной поток и единственную горутину, поэтому и дедлок.
Добавьте в самое начало горутину с бесконечным циклом и слипом и падать перестанет.

UPD
Я бы посоветовал использовать готовое решение, которое использую сам github.com/xeoncross/goworkqueue , либо просто подглядеть как оно сделано.
Ответ написан
dimonchik2013
@dimonchik2013
non progredi est regredi
хе-хе

а зачем канал закрывать?
Ответ написан
Delgus
@Delgus
Я понимаю так - чтобы прекратить выполнение скрипта надо понять что все воркеры закончили работать. Собственно сам go это и делает когда бросает панику fatal error: all goroutines are asleep - deadlock! К сожалению эта паника возникает в рантайме и отловить ее невозможно. Единственный выход самим с определенной периодиочностью проверять стоят наши воркеры или работают. Самое простое решение добавить счетчик работающих горутин и проверять его и канал с задачами что он тоже пуст.

https://play.golang.org/p/K3grU-mmkRb

package main

import (
	"fmt"
	"time"
)

func main() {
	// задачи
	tasks := make(chan int, 2)
	// канал для расчета количества работающих воркеров
	working := make(chan int)
	// канал для остановки скрипта
	done := make(chan struct{})

	for i := 0; i < 2; i++ {
		go func(i int) {
			for x := range tasks {
				working <- 1
				fmt.Println(i, x)
				if x >= 5 {
					tasks <- x - 1
				}
				working <- -1
			}
		}(i)
	}

	// счетчик работающих горутин
	go func() {
		ticker := time.NewTicker(time.Second)
		// количество работающих воркеров
		var count int

		for {
			select {
			case w := <-working:
				count += w
			case <-ticker.C:
				if count == 0 && len(tasks) == 0 {
					close(done)
				}
			}
		}
	}()

	tasks <- 1
	tasks <- 3
	tasks <- 2
	tasks <- 5
	tasks <- 3
	tasks <- 9

	<-done
}
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы