---
id: 208cfd8127284f07337b5f5002f3351d
author: hamori
title: 'Go goroutine(goroutine)működési elve: 2 KB-os változó méretű verem és az M:N GMP ütemező'
description: A párhuzamosság (concurrency) és a párhuzamos végrehajtás (parallelism) közötti különbségtől kezdve bemutatjuk a goroutine-ok 2KB-os kezdeti, dinamikusan változó méretű veremét, a felhasználói szintű kontextusváltást, a GOMAXPROCS-t és a GMP (Goroutine-Machine-Processor) futtatókörnyezeti ütemezési modellt.
language: hu
date: 2025-08-22T13:28:15.766056383Z
path: /blog/posts/goroutine-basics-z3fbe209e
category: concurrency
---

# Goroutine

A Gopher-eknek a golang előnyeiről szólva gyakran előkerül a **párhuzamosság (Concurrency)** témája. Ennek alapja a könnyű és egyszerűen kezelhető **goroutine (goroutine)**. Ezzel kapcsolatban röviden összefoglaltam a lényeget.

### **Concurrency (párhuzamosság) vs Parallelism (párhuzamos végrehajtás)**

Mielőtt rátérnénk a goroutine-ra, először két gyakran összetévesztett fogalmat tisztázok.

- Concurrency: A concurrency arról szól, hogy sok feladatot kezelünk egyszerre. Nem feltétlenül jelenti azt, hogy valóban egyidejűleg futnak, hanem egy strukturális, logikai koncepció, amelyben a munkákat kis egységekre bontjuk, és váltakozva hajtjuk végre őket, így a felhasználó számára úgy tűnik, mintha több feladat egyszerre történne. Egyetlen magon is lehetséges a concurrency.
- Parallelism: A parallelism azt jelenti, hogy „több feladatot egyidejűleg, több magon végezünk”. Szó szerint a munkák párhuzamos végrehajtásáról van szó, amelyben más feladatok egyszerre futnak.

A goroutine a Go futtatókörnyezeti ütemezőn keresztül könnyen megvalósítható concurrency-t biztosít, a `GOMAXPROCS` beállítással pedig természetesen kihasználja a parallelismet is.

A Java többszálú (Multi thread) megoldása, amely széles körben elterjedt, a parallelism reprezentatív fogalma.

# Miért jók a goroutine-ok?

### Könnyűsúlyú (lightweight)

Létrehozási költsége jóval alacsonyabb, mint más nyelveké. Felmerülhet a kérdés, miért használ a golang kevesebbet, és a válasz az, hogy a létrehozást a Go futtatókörnyezete kezeli belsőleg. Ennek oka, hogy könnyűsúlyú logikai szálról van szó, amely kisebb az OS-szál egységénél; kezdeti verme körülbelül 2KB méretű, és a megvalósítástól függően a verem dinamikusan bővülhet és változhat.

Mivel verem egységekben kezelik, a létrehozás és megszüntetés nagyon gyors és olcsó, így akár több millió goroutine futtatása sem jelent terhet. Ennek köszönhetően a Goroutine a futtatókörnyezeti ütemezőnek hála minimalizálhatja az OS kernel beavatkozását.

### Jó teljesítmény (performance)

Először is, mint fentebb leírtam, a Goroutine kevesebb OS kernel beavatkozást igényel, ezért felhasználói szinten (User-Level) történő kontextusváltás költsége alacsonyabb az OS-szál egységéhez képest, így a feladatok gyorsan átválthatók.

Emellett az M:N modellt alkalmazva OS-szálakhoz rendeli és kezeli őket. OS-szálkészletet hozunk létre, így nincs szükség sok szálra, kevesebb szállal is megoldható a feladat. Például rendszerhívás vagy más várakozási állapot esetén a Go futtatókörnyezete az OS-szálon másik goroutine-t futtat, így az OS-szál megállás nélkül, hatékonyan használja a CPU-t, és gyors feldolgozás válik lehetővé.

Ennek következtében a Golang különösen az I/O műveleteknél képes magasabb teljesítményt nyújtani más nyelvekhez képest.

### Tömör (concise)

Nagy előny, hogy ha párhuzamosságra van szükség, egyetlen `go` kulcsszóval egyszerűen indíthatunk el egy függvényt.

Összetett zárakat, például `Mutex`-et és `Semaphore`-t kell használni, és zárak alkalmazásakor szükségszerűen figyelembe kell venni a holtpont (DeadLock) állapotot, ami már a tervezési fázisban összetett lépéseket igényel.

A Goroutine a „ne osszunk meg memóriát a kommunikáció érdekében, hanem kommunikáljunk a memória megosztásáért” elv szerint a `csatornán (Channel)` keresztüli adatátvitelt javasolja, a `SELECT` pedig a csatornákkal együttműködve lehetővé teszi, hogy azok a csatornák kerüljenek először feldolgozásra, amelyeken már készen áll az adat. Emellett a `sync.WaitGroup` használatával egyszerűen megvárhatjuk, amíg több goroutine befejeződik, ami megkönnyíti a munkafolyamatok kezelését. Ezeknek az eszközöknek köszönhetően megelőzhető a szálak közötti adatversenyhelyzet, és biztonságosabban valósítható meg a concurrency.

Továbbá a kontextus (context) segítségével felhasználói szinten (User-Level) szabályozható az élettartam, a megszakítás, az időtúllépés, a határidő és a kérés hatóköre, így bizonyos mértékű stabilitás is biztosítható.

# A Goroutine párhuzamos működése (GOMAXPROCS)

Felmerülhet a kérdés, hogy ha a goroutine concurrency-je jó, vajon támogatja-e a párhuzamosságot is. A mai CPU-k magszáma a korábbiakkal ellentétben már a két számjegyű tartományt is elérheti, és a házi PC-kbe is sok mag kerül.

A Goroutine azonban a párhuzamos feldolgozást is végrehajtja, és ezt a `GOMAXPROCS` teszi lehetővé.

Ha nem állítjuk be a `GOMAXPROCS` értékét, az verziónként eltérően alakul.

1. 1.5 előtt: az alapérték 1, 1-nél nagyobb érték szükség esetén kötelező beállítani, például `runtime.GOMAXPOCS(runtime.NumCPU())` módon
2. 1.5 ~ 1.24: az összes elérhető logikai magszámra változott. Ettől kezdve a fejlesztőnek nincs szüksége beállításra, hacsak nincs jelentős korlátozás
3. 1.25: a konténeres környezetben való elterjedtségéhez méltóan a linuxon lévő cGroup-ot ellenőrzi, és megnézi a konténerre beállított `CPU korlátot`.

   Ebben az esetben, ha a logikai magok száma 10, a CPU-korlát értéke pedig 5, a `GOMAXPROCS` az alacsonyabb értékre, 5-re áll be.

Az 1.25-ös módosítás nagyon jelentős változtatás, mert javult a nyelv használhatósága konténeres környezetben. Ennek következtében elkerülhető a felesleges szállétrehozás és kontextusváltás, így megelőzhető a CPU throttling.

```go
package main

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"
)

func exe(name int, wg *sync.WaitGroup) {
	defer wg.Done()

	fmt.Printf("Goroutine %d: 시작\n", name)
	time.Sleep(10 * time.Millisecond) // 작업 시뮬레이션을 위한 지연
	fmt.Printf("Goroutine %d: 시작\n", name)
}

func main() {
	runtime.GOMAXPROCS(2) // CPU 코어 2개만 사용
	wg := sync.WaitGroup();
  goroutineCount := 10
	wg.Add(goroutineCount)

	for i := 0; i < goroutineCount; i++ {
		go exe(i, &wg)
	}

	fmt.Println("모든 goroutine이 끝날 때까지 대기합니다...")
	wg.Wait()
	fmt.Println("모든 작업이 완료되었습니다.")

}

```

# A Goroutine ütemezője (M:N modell)

Az előző részben említett, OS-szálakhoz rendelést és kezelést az M:N modell valósítja meg. Ha ezt részletesebben nézzük, a goroutine GMP modelljéhez jutunk.

- G (Goroutine): A Go-ban futtatható legkisebb munkaegység
- M (Machine): OS-szál (a tényleges végrehajtási hely)
- P (Processor): A Go futtatókörnyezete által kezelt logikai processzor

Ezek a fogalmak. A P emellett helyi futtatási sorral (Local Run Queue) rendelkezik, és ütemezőként működik, amely a hozzárendelt G-ket az M-hez rendeli. Röviden: a goroutine

A GMP működése a következő lépésekből áll:

1. Amikor egy G (Goroutine) létrejön, hozzárendelésre kerül a P (Processor) helyi futtatási sorához.
2. A P (Processor) a helyi futtatási sorban lévő G (Goroutine)-t hozzárendeli egy M (Machine)-hez.
3. Az M (Machine) visszaadja a G (Goroutine) állapotát: block, complete vagy preempted.
4. Work-Stealing (munkalopás): Ha egy P helyi futtatási sora kiürül, a többi P a globális sort ellenőrzi. Ha ott sincs G (Goroutine), más P (Processor) helyi munkáját lopja el, így minden M megállás nélkül működik.
5. Rendszerhívás kezelése (Blocking): Ha a G (Goroutine) futás közben blokkolódik, az M (Machine) várakozó állapotba kerül, ekkor a P (Processor) leválasztódik a blokkolt M-ről, és egy másik M-hez kapcsolódva futtatja a következő G (Goroutine)-t. Ilyenkor az I/O művelet várakozási idején sincs CPU-pazarlás.
6. Ha egy G (Goroutine) hosszú ideig foglalja a processzort (preempted), a többi G (Goroutine) is kap futási lehetőséget.

A Golang GC (Garbage Collector) működése is a goroutine-okon belül történik, így a memória párhuzamosan tisztítható úgy, hogy az alkalmazás futása a lehető legkisebb mértékben szakad meg (STW), ami hatékonyan használja a rendszererőforrásokat.

Végül, a Golang a nyelv egyik erős előnye, és ezen kívül még sok más is van benne, ezért kívánom, hogy sok fejlesztő élvezze a Go használatát.

Köszönöm a figyelmet.
