2016-02-18 5 views
1

Мне нужна помощь в понимании того, как использовать goroutines в этой проблеме. Я опубликую только некоторые фрагменты кода, но если вы хотите сделать глубокий взгляд, вы можете это проверить. hereПроблемы с реализацией Goroutine

В принципе, у меня есть функция-дистрибьютор, которая получает многократно запрошенный срез и каждый раз, когда функция он должен распространять этот запрос среди других функций для фактического разрешения запроса. И я пытаюсь создать канал и запустить эту функцию для разрешения запроса на новый goroutine, поэтому программа может обрабатывать запросы одновременно.

Как функция распределения называется:

// Run trigger the system to start receiving requests 
func Run() { 

    // Since the programs starts here, let's make a channel to receive requests 
    requestCh := make(chan []string) 
    idCh := make(chan string) 

    // If you want to play with us you need to register your Sender here 
    go publisher.Sender(requestCh) 
    go makeID(idCh) 
    // Our request pool 
    for request := range requestCh { 

     // add ID 
     request = append(request, <-idCh) 

     // distribute 
     distributor(request) 
    } 

    // PROBLEM 
    for result := range resultCh { 
     fmt.Println(result) 
    } 
} 

Распределить саму функцию:

// Distribute requests to respective channels. 
// No waiting in line. Everybody gets its own goroutine! 
func distributor(request []string) { 

    switch request[0] { 

    case "sum": 
     arithCh := make(chan []string) 
     go arithmetic.Exec(arithCh, resultCh) 
     arithCh <- request 
    case "sub": 
     arithCh := make(chan []string) 
     go arithmetic.Exec(arithCh, resultCh) 
     arithCh <- request 
    case "mult": 
     arithCh := make(chan []string) 
     go arithmetic.Exec(arithCh, resultCh) 
     arithCh <- request 
    case "div": 
     arithCh := make(chan []string) 
     go arithmetic.Exec(arithCh, resultCh) 
     arithCh <- request 
    case "fibonacci": 
     fibCh := make(chan []string) 
     go fibonacci.Exec(fibCh, resultCh) 
     fibCh <- request 
    case "reverse": 
     revCh := make(chan []string) 
     go reverse.Exec(revCh, resultCh) 
     revCh <- request 
    case "encode": 
     encCh := make(chan []string) 
     go encode.Exec(encCh, resultCh) 
     encCh <- request 
    } 
} 

и функция fibonacci.Exec, чтобы показать, как я пытаюсь вычислить Фибоначчи дал запрос полученных на fibCh и отправляющих результат результата через resultCh.

func Exec(fibCh chan []string, result chan map[string]string) { 

    fib := parse(<-fibCh) 
    nthFibonacci(fib) 

    result <- fib 
} 

До сих пор, в функции Run, когда я пробегаю resultCh я получаю результаты, но и тупиковые. Но почему? Кроме того, я полагаю, что я должен использовать функцию waitGroup, чтобы дождаться завершения goroutines, но я не уверен, как реализовать это, поскольку я ожидаю получить непрерывный поток запросов. Я был бы признателен за помощь в понимании того, что я делаю неправильно, и способа ее решения.

ответ

1

Я не вникаю в детали реализации вашего приложения, но в основном, как мне кажется, вы можете использовать шаблон workers.

Использование шаблона workers Несколько горутин могут считывать с одного канала, распределяя объем работы между ядрами ЦП, следовательно, имя рабочего. В Go этот шаблон легко реализовать - просто запустите несколько goroutines с каналом в качестве параметра и просто отправьте значения этому каналу. Распространение и мультиплексирование будут выполняться автоматически с помощью Go runtime.

Вот простая реализация рабочих шаблона:

package main 

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

func worker(tasksCh <-chan int, wg *sync.WaitGroup) { 
    defer wg.Done() 
    for { 
     task, ok := <-tasksCh 
     if !ok { 
      return 
     } 
     d := time.Duration(task) * time.Millisecond 
     time.Sleep(d) 
     fmt.Println("processing task", task) 
    } 
} 

func pool(wg *sync.WaitGroup, workers, tasks int) { 
    tasksCh := make(chan int) 

    for i := 0; i < workers; i++ { 
     go worker(tasksCh, wg) 
    } 

    for i := 0; i < tasks; i++ { 
     tasksCh <- i 
    } 

    close(tasksCh) 
} 

func main() { 
    var wg sync.WaitGroup 
    wg.Add(36) 
    go pool(&wg, 36, 50) 
    wg.Wait() 
} 

Еще один полезный ресурс, как вы можете использовать WaitGroup ждать всех goroutines, чтобы закончить выполнение, прежде чем продолжить (следовательно, не ловушку в тупик) это хорошая статья:

http://nathanleclaire.com/blog/2014/02/15/how-to-wait-for-all-goroutines-to-finish-executing-before-continuing/

И очень базовая реализация этого:

Go playground

Если вы не хотите, чтобы изменить реализацию использовать worker шаблон, может быть бы хорошей идеей, чтобы использовать другой канал, чтобы обозначить конец goroutine исполнения, так как тупиковая происходит, когда нет приемник принять отправленное сообщение через небуферизованный канал.

done := make(chan bool) 
//..... 
done <- true //Tell the main function everything is done. 

Поэтому, когда вы получаете сообщение, вы отмечаете выполнение как выполненное, устанавливая значение канала равным true.

+0

Я ценю ваш ответ, но, хотя ваше предложение использовать рабочий шаблон здесь (вещь, которую я должен был рассмотреть с самого начала). Считаете ли вы, что теперь можно указать улучшения моей старой реализации, чтобы начать все заново? Я говорю это, потому что, пока я не доберусь до функции Фибоначчи, все работает нормально. Проблема, похоже, является утечкой goroutine, вызванной тем, как я запускаю фибоначчи. –

+0

Все, что мне нужно, это предоставить запрос, поступающий к функции распределения, одновременно запустить функцию фибоначчи, и как только появится результат этой процедуры, отправьте этот результат в resultCh, чтобы я мог повторно отправить клиенту и распечатать его там , –

+0

Можете ли вы опубликовать сообщение об ошибке? –

Смежные вопросы