Как работают goroutines? (или: отношение goroutines и потоков ОС)

как другие goroutines продолжают выполнять при вызове syscall? (при использовании GOMAXPROCS=1)
Насколько мне известно, при вызове syscall поток отказывается от управления до тех пор, пока syscall не вернется. Как можно достичь этого параллелизма, не создавая системный поток для блокировки на syscall goroutine?

с документация:

горутины

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

Goroutines мультиплексируются на несколько потоков ОС, поэтому, если нужно блок, например, во время ожидания ввода/вывода, другие продолжают работать. Их конструкция прячет много из сложностей творения потока и управление.

4 ответов


Если goroutine блокирует, среда выполнения запустит новый поток ОС для обработки других goroutines, пока блокирующий не прекратит блокировку.

ссылки : https://groups.google.com/forum/#!тема/golang-орехи/2IdA34yR8gQ


хорошо, вот что я узнал: Когда вы делаете необработанные syscalls, Go действительно создает поток для блокировки goroutine. Например, рассмотрим следующий код:

package main

import (
        "fmt"
        "syscall"
)

func block(c chan bool) {
        fmt.Println("block() enter")
        buf := make([]byte, 1024)
        _, _ = syscall.Read(0, buf) // block on doing an unbuffered read on STDIN
        fmt.Println("block() exit")
        c <- true // main() we're done
}

func main() {
        c := make(chan bool)
        for i := 0; i < 1000; i++ {
                go block(c)
        }
        for i := 0; i < 1000; i++ {
                _ = <-c
        }
}

при запуске Ubuntu 12.04 сообщил о 1004 потоках для этого процесса.

С другой стороны, при использовании HTTP-сервера Go и открытии 1000 сокетов для него было создано только 4 потока операционной системы:

package main

import (
        "fmt"
        "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
}

func main() {
        http.HandleFunc("/", handler)
        http.ListenAndServe(":8080", nil)
}

Итак, это смесь между IOLoop и потоком на блокировку системный вызов.


не может. Существует только 1 goroutine, который может работать в то время, когда GOMAXPROCS=1, независимо от того, делает ли этот один goroutine системный вызов или что-то еще.

однако большинство блокирующих системных вызовов, таких как socket I/O, ожидание таймера не блокируются при системном вызове при выполнении с Go. Они мультиплексируются средой выполнения Go на epoll, kqueue или аналогичные средства, которые ОС предоставляет для мультиплексирования ввода-вывода

для других видов блокировки системных вызовов, которые невозможно мультиплексировать с чем-то вроде epoll, Go не порождает новый поток ОС, независимо от настройки GOMAXPROCS (хотя это было состояние в Go 1.1, я не уверен, что ситуация изменилась)


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

Как объясняет приведенная вами документация, goroutine не является потоком: это просто функция, выполняемая в потоке, который выделен кусок пространства стека. Если ваш процесс имеет более одного потока, Эта функция может быть выполнена либо нить. Итак, goroutine, который блокирует причина или другое (syscall, I / O, синхронизация) могут быть впущены в свой поток, в то время как другие процедуры могут быть выполнены другим.