Goroutine үндэс
Go хэлийг бусад хэлнүүдээс тодотгон ялгаруулдаг хамгийн гол онцлог бол concurrency — нэгэн зэрэг олон ажлыг гүйцэтгэх чадвар. Goroutine бол Go-ийн concurrency-ийн суурь хэрэгсэл юм.
Goroutine гэж юу вэ?
Goroutine нь Go runtime-ийн удирдан зохицуулдаг, маш хөнгөн гүйцэтгэлийн нэгж. Thread-тэй адилхан боловч хамаагүй хямд:
- Нэг OS thread — хэдэн МБ санах ой
- Нэг goroutine — ердөө ~2 КБ санах ой
Нэг программ дотор мянга, тэр ч байтугай сая goroutine ажиллуулж болно.
go түлхүүр үг
Функцийн өмнө go бичихэд тухайн функц шинэ goroutine дотор ажиллана:
package main
import (
"fmt"
"time"
)
func sayHello(name string) {
fmt.Printf("Сайн уу, %s!\n", name)
}
func main() {
go sayHello("Бат") // Goroutine дотор
go sayHello("Дорж") // Горутин дотор
sayHello("Болд") // Үндсэн goroutine дотор
// Goroutine-үүд дуусахыг хүлээх (түр шийдэл)
time.Sleep(100 * time.Millisecond)
}
go sayHello("Бат") нь функцийг дуусахыг хүлээхгүйгээр шууд дараагийн мөрт шилждэг.
Goroutine-ийн дараалал тодорхойгүй
Goroutine-үүдийн гүйцэтгэлийн дараалал нь урьдчилан тодорхойгүй. Дараах кодыг хэд хэдэн удаа ажиллуулахад өөр өөр дараалалтай хэвлэгдэж болно:
package main
import (
"fmt"
"time"
)
func printNumbers(id int) {
for i := 1; i <= 3; i++ {
fmt.Printf("Goroutine %d: %d\n", id, i)
}
}
func main() {
go printNumbers(1)
go printNumbers(2)
go printNumbers(3)
time.Sleep(200 * time.Millisecond)
fmt.Println("Дууслаа")
}
Гаралт дараалал өөрчлөгдөж болно — энэ нь бүрэн хэвийн зүйл. Runtime нь goroutine-үүдийг тохиромжтой гэж үзсэн дарааллаар гүйцэтгэнэ.
time.Sleep — муу шийдэл
Дээрх жишээнүүдэд time.Sleep ашиглаж горутинүүдийг хүлээсэн нь зохих шийдэл биш. Ямар хугацаа хүлээх нь тодорхойгүй.
Зөв шийдэл нь sync.WaitGroup (дараагийн хичээлд) эсвэл channel (27-р хичээлд) юм. Гэхдээ горутинийг ойлгохын тулд эхлээд time.Sleep ашиглан туршиж болно.
Горутин дотор анонимная функц
Функц тодорхойлоод шууд горутин болгож болно:
package main
import (
"fmt"
"time"
)
func main() {
for i := 1; i <= 5; i++ {
i := i // Маш чухал! Утгыг хуулах
go func() {
fmt.Printf("Ажил %d дууслаа\n", i)
}()
}
time.Sleep(200 * time.Millisecond)
}
i := i гэж давтан бичих нь чухал. Ингэхгүй бол бүх горутин нэг i хувьсагчийг хуваалцаж, loop дуусахаас өмнө утга өөрчлөгддөг буруу үр дүн гарна.
Goroutine ба main функц
main функц дуусахад бүх goroutine-үүд зогсдог — дуусаагүй ч байсан:
package main
import "fmt"
func longTask() {
for i := 0; i < 1000; i++ {
fmt.Println("Ажиллаж байна...", i)
}
}
func main() {
go longTask()
fmt.Println("Main дууслаа — програм гарна")
// longTask ажиллаж байсан ч энд зогсоно
}
Иймээс горутинүүдийг хүлээх механизм заавал хэрэгтэй.
Дараагийн хичээлд:
Горутинүүдийн хооронд өгөгдөл дамжуулах хэрэгсэл болох channel-ийг сурна. Channel нь Go-ийн "горутинүүд харилцдаг гол гүүр" юм.