Další informace o goroutinách

Dokončeno

Souběžnost je složení nezávislých aktivit, jako je například práce, kterou webový server provádí, když se zabývá více uživatelskými požadavky současně, ale autonomním způsobem. Souběžnost je dnes přítomna v mnoha programech. Jedním zpříkladůch

Go má dva styly pro psaní souběžných programů. Jedním z nich je tradiční styl, který jste mohli použít v jiných jazycích s vlákny. V tomto modulu se dozvíte o stylu Go, kde se hodnoty předávají mezi nezávislými aktivitami známými jako goroutiny ke komunikaci procesů.

Pokud se o souběžnosti učíte poprvé, doporučujeme, abyste strávili delší dobu kontrolou jednotlivých částí kódu, které napíšeme do praxe.

Přístup ke souběžnosti

Největší problém při psaní souběžných programů je obvykle sdílení dat mezi procesy. Go používá jiný přístup než jiné programovací jazyky s komunikací, protože Go předává data dozadu přes kanály. Tento přístup znamená, že k datům má přístup jenom jedna aktivita (goroutine) a neexistuje žádný stav časování podle návrhu. Když se v tomto modulu dozvíte o goroutinách a kanálech, lépe porozumíte přístupu ke souběžnosti Go.

Přístup go lze shrnout v následujícím hesle: "Nekomunikujte sdílením paměti; místo toho sdílejte paměť komunikací." Tento přístup probereme v následujících částech, ale další informace najdete také v příspěvku Na blogu Go Sdílet paměť pomocí komunikace.

Jak jsme zmínili dříve, Go také zahrnuje primitivy souběžnosti nízké úrovně. V tomto modulu ale probereme jen idiomaticský přístup Go pro souběžnost.

Začněme tím, že prozkoumáme goroutiny.

Goroutiny

Goroutine je souběžná aktivita v odlehčeném vlákně, ne tradiční aktivita, kterou máte v operačním systému. Předpokládejme, že máte program, který zapisuje do výstupu a další funkci, která počítá věci, jako je sčítání dvou čísel. Souběžný program může mít několik goroutin, které současně volají obě funkce.

Můžeme říci, že první goroutinou, kterou program spustí, je main() funkce. Pokud chcete vytvořit další goroutinu, musíte před voláním funkce použít go klíčové slovo, například v tomto příkladu:

func main(){
    login()
    go launch()
}

Alternativně zjistíte, že mnoho programů, jako je použití anonymních funkcí k vytváření goroutin, jako v tomto kódu:

func main(){
    login()
    go func() {
        launch()
    }()
}

Abychom viděli goroutiny v akci, napište souběžný program.

Zápis souběžného programu

Vzhledem k tomu, že se chceme soustředit jenom na souběžnou část, použijeme existující program, který zkontroluje, jestli koncový bod rozhraní API reaguje nebo ne. Tady je kód:

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    start := time.Now()

    apis := []string{
        "https://management.azure.com",
        "https://dev.azure.com",
        "https://api.github.com",
        "https://outlook.office.com/",
        "https://api.somewhereintheinternet.com/",
        "https://graph.microsoft.com",
    }

    for _, api := range apis {
        _, err := http.Get(api)
        if err != nil {
            fmt.Printf("ERROR: %s is down!\n", api)
            continue
        }

        fmt.Printf("SUCCESS: %s is up and running!\n", api)
    }

    elapsed := time.Since(start)
    fmt.Printf("Done! It took %v seconds!\n", elapsed.Seconds())
}

Při spuštění předchozího kódu získáte následující výstup:

SUCCESS: https://management.azure.com is up and running!
SUCCESS: https://dev.azure.com is up and running!
SUCCESS: https://api.github.com is up and running!
SUCCESS: https://outlook.office.com/ is up and running!
ERROR: https://api.somewhereintheinternet.com/ is down!
SUCCESS: https://graph.microsoft.com is up and running!
Done! It took 1.658436834 seconds!

Nic není z obyčejného tady, ale můžeme to udělat lépe. Možná můžeme zkontrolovat všechny weby najednou? Místo téměř dvou sekund by program mohl dokončit méně než 500 ms.

Všimněte si, že část kódu, kterou potřebujeme spustit souběžně, je ta, která provádí volání HTTP na web. Jinými slovy, musíme vytvořit goroutine pro každé rozhraní API, které program kontroluje.

Abychom vytvořili goroutine, musíme před voláním funkce použít go klíčové slovo. Ale nemáme tam funkci. Pojďme tento kód refaktorovat a vytvořit novou funkci, například takto:

func checkAPI(api string) {
    _, err := http.Get(api)
    if err != nil {
        fmt.Printf("ERROR: %s is down!\n", api)
        return
    }

    fmt.Printf("SUCCESS: %s is up and running!\n", api)
}

Všimněte si, že už klíčové slovo nepotřebujeme continue , protože nejsme ve for smyčce. K zastavení toku provádění funkce použijeme return klíčové slovo. Teď potřebujeme upravit kód ve main() funkci, abychom vytvořili goroutine na rozhraní API, například takto:

for _, api := range apis {
    go checkAPI(api)
}

Spusťte program znovu a podívejte se, co se stane.

Zdá se, že program už nekontroluje rozhraní API, že? Může se zobrazit něco jako v následujícím výstupu:

Done! It took 1.506e-05 seconds!

To bylo rychlé! Co se stalo? Zobrazí se konečná zpráva s oznámením, že program byl dokončen, protože Go vytvořil goroutine pro každý web ve smyčce a šel okamžitě na další řádek.

I když funkce nevypadá, checkAPI že je spuštěná, běží. Prostě neměl čas na dokončení. Všimněte si, co se stane, když za smyčku zahrnete časovač režimu spánku, například takto:

for _, api := range apis {
    go checkAPI(api)
}

time.Sleep(3 * time.Second)

Když teď program znovu spustíte, může se zobrazit výstup podobný tomuto:

ERROR: https://api.somewhereintheinternet.com/ is down!
SUCCESS: https://api.github.com is up and running!
SUCCESS: https://management.azure.com is up and running!
SUCCESS: https://dev.azure.com is up and running!
SUCCESS: https://outlook.office.com/ is up and running!
SUCCESS: https://graph.microsoft.com is up and running!
Done! It took 3.002114573 seconds!

Vypadá to, že to funguje, že? No, ne přesně. Co když chcete do seznamu přidat nový web? Možná tři sekundy nestačí. Jak bys věděl? Nemůžete. Musí existovat lepší způsob, a to je to, co probereme v další části, když mluvíme o kanálech.