Параллелизм против параллелизма при выполнении goroutine - PullRequest
0 голосов
/ 24 октября 2018

Довольно наивный вопрос.Я проходил курс обучения go-concurrency и наткнулся на этот https://tour.golang.org/concurrency/4.

. Я изменил код, добавив оператор печати в функцию fibonacci.Так что код выглядит примерно так:

package main

import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
        fmt.Println("here")
    }
    close(c)
}

func main() {
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    for i := range c {
        fmt.Println(i)
    }
}

И я получил это как вывод

here
here
here
here
here
here
here
here
here
here
0
1
1
2
3
5
8
13
21
34

Я ожидал, что here и numbers будут чередоваться.(Так как процедура выполняется одновременно) Я думаю, что мне не хватает чего-то базового в go-рутинах.Не совсем уверен, что, хотя.

Ответы [ 2 ]

0 голосов
/ 25 октября 2018

Я думаю, что вы наблюдаете, что Go имеет свой собственный планировщик, и в то же время существует различие между «параллелизмом» и «параллелизмом».По словам Роба Пайка: Параллелизм - это не параллелизм

Горутины намного легче, чем потоки ОС, и ими управляют в «пользовательской среде» (в процессе Go), а не в операционнойсистема.В некоторых программах запущено много тысяч (даже десятков тысяч) подпрограмм, хотя, безусловно, будет выделено гораздо меньше потоков операционной системы.(Это одно из главных преимуществ Go в асинхронных программах с большим количеством подпрограмм)

Поскольку ваша программа очень проста и канал буферизован, она не блокируется при записи в канал:

c <- x

фибоначчи не прерывается до завершения короткого цикла.

Даже fmt.Println("here") не детерминированно вводит вытеснение - я сам кое-что узнал, написав этот ответ.Он буферизован, как аналогичные printf и scanf из C .(см. исходный код https://github.com/golang/go/blob/master/src/fmt/print.go)

. Для интереса, если вы хотите искусственно контролировать количество потоков ОС, вы можете установить переменную среды GOMAXPROCS в командной строке:

~$ GOMAXPROCS=1 go run main.go

Однако, с вашей простой программой, вероятно, не будет заметной разницы , потому что среда выполнения Go по-прежнему отлично способна планировать множество процедур для одного потока ОС.

ДляНапример, вот небольшая вариация вашей программы: уменьшив буфер канала (5), но повторяя его 10 раз, мы вводим точку, в которой процедура fibonacci go может (но не обязательно) быть прерванным, где он может заблокировать хотя бы один раз при записи в канал:

package main

import (
    "fmt"
)

func fibonacci(n int, c chan int) {
    x, y := 0, 1
    for i := 0; i < n; i++ {
        c <- x
        x, y = y, x+y
        fmt.Println("here")
    }
    close(c)
}

func main() {
    c := make(chan int, 5)
    go fibonacci(cap(c)*2, c)
    for i := range c {
        fmt.Println(i)
    }
}

~$ GOMAXPROCS=1 go run main.go
here
here
here
here
here
here
0
1
1
2
3
5
8
here
here
here
here
13
21
34

Длинное объяснение здесь , краткое объяснение состоит в том, что существует множество причин, по которым процедура goможет временно заблокировать, и для планировщика go это идеальные возможности для планирования выполнения другой подпрограммы go.

0 голосов
/ 25 октября 2018

Несколько вещей здесь.

  1. У вас есть 2 goroutines, один работает main() и один работает fibonacci().Поскольку это небольшая программа, у планировщика go нет веских причин не запускать их один за другим в одном и том же потоке, поэтому это происходит последовательно, хотя это не гарантируется.Поскольку goroutine в main() ожидает chan, подпрограмма fibonacci() запланирована первой.Важно помнить, что goroutines - это не потоки, а подпрограммы, которые планировщик go запускает в потоках в соответствии со своими предпочтениями.

  2. Поскольку вы передаете длину буферизованного канала в fibonacci(), почти наверняка ( никогда не полагается на это поведение ) будет cap(c) heres печатается, после чего channel заполняется, цикл for завершается, close отправляется на chan, а goroutine заканчивается.Затем запланирована main() процедура и cap(c) Фибоначчи.Если буферизованный chan был заполнен, то main() был бы перенесен: https://play.golang.org/p/_IgFIO1K-Dc

  3. Спя, вы можете сказать, что планировщик go должен отказаться от управления.Но на практике никогда не делай этого.Перестройте каким-либо образом или, если необходимо, используйте группу ожидания.См .: https://play.golang.org/p/Ln06-NYhQDj

  4. Я думаю, вы пытаетесь это сделать: https://play.golang.org/p/8Xo7iCJ8Gj6

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...