Задать вопрос

Как заставить горутины запускаться в порядке очередности?

Добрый день!

Есть следующий код
package main

import (
	"fmt"
	"sync"
)

func main() {
	wg := &sync.WaitGroup{}
	ch := make(chan string)
	mu := sync.Mutex{}

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(group *sync.WaitGroup, i int) {
			defer group.Done()

			mu.Lock()
			ch <- fmt.Sprintf("Goroutine %d", i)
			mu.Unlock()
		}(wg, i)
	}

	for i := 0; i < 5; i++ {
		select {
		case s := <-ch:
			fmt.Println(s)
		}
	}

	wg.Wait()
}


Есть проблема, что значения Goroutine N печатаются не в порядке очереди, а в порядке очереди запуска горутин. Всю голову уже сломал, но не могу придумать, как обеспечить гарантию порядка в данном случае?
  • Вопрос задан
  • 531 просмотр
Подписаться 4 Простой 5 комментариев
Пригласить эксперта
Ответы на вопрос 2
2ord
@2ord
Сначала запускаем воркеры, потом создаем задачи. По окончанию задач надо закрыть канал.
package main

import (
	"fmt"
	"sync"
	"time"
)

const (
	jobsNum    = 5
	workersNum = 2
)

type Job struct {
	id int
}

func worker(id int, jobChan <-chan Job, wg *sync.WaitGroup) {
	for job := range jobChan {
		process(id, job, wg)
	}
}

func process(id int, job Job, wg *sync.WaitGroup) {
	defer wg.Done()
	fmt.Printf("Worker %d, Job start: %d\n", id, job.id)
	time.Sleep(1 * time.Second)
	fmt.Printf("Worker %d, Job end: %d\n", id, job.id)
}

func main() {
	fmt.Println("Start")
	jobChan := make(chan Job, 10)

	var wg sync.WaitGroup

	wg.Add(jobsNum)

	// start the workers
	for i := 0; i < workersNum; i++ {
		go worker(i, jobChan, &wg)
	}

	// enqueue jobs
	for i := 0; i < jobsNum; i++ {
		jobChan <- Job{
			id: i,
		}
	}
	close(jobChan)

	wg.Wait()
	fmt.Println("Done")
}

вывод

Start
Worker 1, Job start: 1
Worker 0, Job start: 0
Worker 0, Job end: 0
Worker 0, Job start: 2
Worker 1, Job end: 1
Worker 1, Job start: 3
Worker 0, Job end: 2
Worker 0, Job start: 4
Worker 1, Job end: 3
Worker 0, Job end: 4
Done
Ответ написан
Комментировать
@ykalchevskiy
Если этот код является лишь шаблоном задачи с собеседования, который предполагалось расширить, то можно добавить условную переменную и обычную переменную для текущего счетчика:

package main

import (
	"fmt"
	"sync"
)

func main() {
	wg := &sync.WaitGroup{}
	ch := make(chan string)
	mu := sync.Mutex{}
	s := 0
	c := sync.NewCond(&mu)

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(group *sync.WaitGroup, i int) {
			defer group.Done()

			mu.Lock() // c.L.Lock()
			for s != i {
				c.Wait()
			}
			ch <- fmt.Sprintf("Goroutine %d", i)
			s += 1
			c.Broadcast()
			mu.Unlock() // c.L.Unlock()
		}(wg, i)
	}

	c.Broadcast()

	for i := 0; i < 5; i++ {
		select {
		case s := <-ch:
			fmt.Println(s)
		}
	}

	wg.Wait()
}
Ответ написан
Ваш ответ на вопрос

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

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