Skip to content

Instantly share code, notes, and snippets.

@jtprogru
Created July 15, 2022 20:38
Show Gist options
  • Save jtprogru/cd5fcfd4f07e3486cf25c903cd5b73f6 to your computer and use it in GitHub Desktop.
Save jtprogru/cd5fcfd4f07e3486cf25c903cd5b73f6 to your computer and use it in GitHub Desktop.

Пул обработчиков

Продолжаем работать с функциями-говорилками. На предыдущем шаге мы заставили их обрабатывать исходные данные в две горутины, с помощью пула:

func main() {
    phrases := []string{
        // ...
    }

    // пул идентификаторов для 2 горутин
    pool := make(chan int, 2)
    pool <- 1
    pool <- 2

    for _, phrase := range phrases {
        // получаем идентификатор из пула,
        // если есть свободные
        id := <-pool
        go say(pool, id, phrase)
    }

    // дожидаемся, пока все горутины закончат работу
    // (то есть все идентификаторы вернутся в пул)
    <-pool
    <-pool
}
func say(pool chan<- int, id int, phrase string) {
    for _, word := range strings.Fields(phrase) {
        // ...
    }
    // возвращаем идентификатор в пул
    pool <- id
}

Это работает, но как-то... кривовато. Во-первых, получился пул строго на две горутины (а вдруг понадобится десять?). Во-вторых, логика пула размазана по всей программе — это чревато ошибками.

Хочется, чтобы функция say() ничего не знала о пуле:

func say(id int, phrase string) {
    for _, word := range strings.Fields(phrase) {
        // ...
    }
}

А функция main() чтобы выглядела так:

func main() {
    phrases := []string{
        // ...
    }

    handle, wait := makePool(2, say)
    for _, phrase := range phrases {
        handle(phrase)
    }
    wait()
}

Конструктор makePool() создает пул на указанное количество обработчиков. Он также принимает функцию-обработчик, которую будет вызывать позже. Конструктор возвращает две функции:

  • handle() выбирает токен из пула и обрабатывает переданную фразу через функцию-обработчик;
  • wait() дожидается, пока все токены вернутся в пул;

Вот шаблон makePool():

func makePool(n int, handler func(int, string)) (func(string), func()) {
    // создайте пул на n обработчиков
    // используйте имя pool для канала
    // определите функции handle() и wait()

    // handle() выбирает токен из пула
    // и обрабатывает переданную фразу через handler()

    // wait() дожидается, пока все токены вернутся в пул

    return handle, wait
}

Реализуйте внутренности.

Полный код — в песочнице. Отправляйте в качестве решения только фрагмент, отмеченный комментариями «начало решения» и «конец решения».

песочница

Sample Input:
Sample Output:
PASS
package main
import (
"fmt"
"math/rand"
"strings"
"time"
)
// say prints the phrase on behalf of the worker
func say(id int, phrase string) {
for _, word := range strings.Fields(phrase) {
fmt.Printf("Worker #%d says: %s...\n", id, word)
dur := time.Duration(rand.Intn(100)) * time.Millisecond
time.Sleep(dur)
}
}
// makePool creates a pool for n workers
// returns handle and wait functions
func makePool(n int, handler func(int, string)) (func(string), func()) {
// начало решения
// создайте пул на n обработчиков
// используйте для канала имя pool и тип chan int
// определите функции handle() и wait()
// handle() выбирает токен из пула
// и обрабатывает переданную фразу через handler()
// wait() дожидается, пока все токены вернутся в пул
// конец решения
return handle, wait
}
func main() {
phrases := []string{
"go is awesome",
"cats are cute",
"rain is wet",
"channels are hard",
"floor is lava",
}
handle, wait := makePool(2, say)
for _, phrase := range phrases {
handle(phrase)
}
wait()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment