Подтвердить что ты не робот

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

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

Из документации:

Goroutines

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

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

4b9b3361

Ответ 1

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

Ссылка: https://groups.google.com/forum/#!topic/golang-nuts/2IdA34yR8gQ

Ответ 2

Хорошо, вот что я узнал: Когда вы делаете необработанные системные вызовы, 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 потоках для этого процесса.

С другой стороны, при использовании Go HTTP-сервера и открытии ему 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 и потоком для системного вызова блокировки.

Ответ 3

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

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

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

Ответ 4

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

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