Hoe werken goroutines? (of: relatie tussen goroutines en OS-threads)

Hoe kunnen andere goroutines blijven uitvoeren terwijl ze een syscall aanroepen? (bij gebruik van GOMAXPROCS=1)
Voor zover ik weet, geeft de thread de controle op bij het aanroepen van een syscall totdat de syscall terugkeert.
Hoe kan Go deze gelijktijdigheid bereiken zonder een systeemthread te maken per blocking-on-syscall-goroutine?

Uit de documentatie:

Goroutines

Ze worden goroutines genoemd omdat de bestaande termen – threads,
coroutines, processen, enzovoort – brengen onnauwkeurige connotaties over. EEN
goroutine heeft een eenvoudig model: het is een functie die gelijktijdig wordt uitgevoerd
met andere goroutines in dezelfde adresruimte. Het is lichtgewicht,
kost weinig meer dan de toewijzing van stapelruimte. En de stapels
begin klein, dus ze zijn goedkoop, en groeien door toe te wijzen (en vrij te maken)
heap opslag zoals vereist.

Goroutines worden gemultiplext op meerdere OS-threads, dus als dat zou moeten
blok, zoals tijdens het wachten op I/O, andere blijven draaien. Hun
ontwerp verbergt veel van de complexiteit van het maken van threads en
beheer.


Antwoord 1, autoriteit 100%

Als een goroutine blokkeert, start de runtime een nieuwe OS-thread om de andere goroutines af te handelen totdat de blokkerende stopt met blokkeren.

Referentie: https://groups.google.com/forum/#!topic/golang -noten/2IdA34yR8gQ


Antwoord 2, autoriteit 64%

Ok, dus dit is wat ik heb geleerd:
Wanneer je onbewerkte syscalls doet, maakt Go inderdaad een thread per blokkerende goroutine. Beschouw bijvoorbeeld de volgende code:

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
        }
}

Tijdens het draaien rapporteerde Ubuntu 12.04 1004 threads voor dat proces.

Aan de andere kant, bij het gebruik van Go’s HTTP-server en het openen van 1000 sockets ervoor, werden er slechts 4 besturingssysteemthreads gemaakt:

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)
}

Het is dus een mix tussen een IOLoop en een thread per blokkerende systeemaanroep.


Antwoord 3, autoriteit 40%

Dat kan niet. Er kan maar 1 goroutine actief zijn op een moment dat GOMAXPROCS=1, of die ene goroutine nu een systeemaanroep doet of iets anders.

De meeste blokkerende systeemaanroepen, zoals socket I/O, die wachten op een timer, worden echter niet geblokkeerd bij een systeemaanroep wanneer ze worden uitgevoerd vanuit Go. Ze worden gemultiplext door de Go-runtime naar epoll, kqueue of soortgelijke faciliteiten die het besturingssysteem biedt voor het multiplexen van I/O.

Voor andere soorten blokkerende systeemaanroepen die niet kunnen worden gemultiplext met iets als epoll, brengt Go een nieuwe OS-thread voort, ongeacht de GOMAXPROCS-instelling (hoewel dat de status was in Go 1.1, weet ik niet zeker of de situatie is gewijzigd)


Antwoord 4, autoriteit 12%

Je moet onderscheid maken tussen processornummer en threadnummer: je kunt meer threads hebben dan fysieke processors, dus een multi-threadproces kan nog steeds worden uitgevoerd op een enkele kernprocessor.

Als de documentatie die u heeft uitgelegd, is een Goroutine geen thread: het is slechts een functie uitgevoerd in een draad die een stuk stapelruimte is opgedragen. Als uw proces meer dan één draad heeft, kan deze functie worden uitgevoerd door een draad. Dus een goroutine die met een reden of een andere reden blokkeert (Sysscall, I / O, synchronisatie) kan in zijn draad worden verhuurd terwijl andere routines door een ander kunnen worden uitgevoerd.


5, Autoriteit 12%

Ik heb een artikel geschreven dat uitlegt hoe ze werken met voorbeelden en diagrammen. Aarzel niet om er een kijkje te nemen: https://osmh.dev/posts/ Goroutines-under-the-kap


6

Van documentatie van runtime :

De GoMAXPROCS-variabele beperkt het aantal besturingssysteemdraden dat de GO-code van het gebruikersniveau gelijktijdig kan uitvoeren. Er is geen limiet aan het aantal threads dat kan worden geblokkeerd in systeemroepen namens GO-code; die tellen niet mee tegen de GomaxProcs-limiet.

Dus wanneer een OS-draad is geblokkeerd voor Sysscall, kan een andere thread worden gestart – en GOMAXPROCS = 1is nog steeds tevreden. De twee threads worden niet tegelijkertijd uitgevoerd.

Other episodes