Middle+
გაამბეთ Go-ში goroutine-ების დაგეგმვის შესახებ.
sobes.tech AI
პასუხი AI-სგან
Go ენის გორუტინების დაგეგმვა იყენებს M:N მოდელს, სადაც M გორუტინები მაპირდება N ოპერაციული სისტემის ნაკადზე. იგი ეფექტურად ცვლის გორუტინებს ნაკადებს შორის, განახორციელებს წინასწარ გაჩერების (preemption points) მეშვეობით.
GPM მოდელის ძირითადი კომპონენტები:
- G (Goroutine): კოდის შესრულების ერთეულის აბსტრაქცია, მსუბუქი ნაკადი, რომელსაც მართავს Go-ის დაგეგმვა.
- P (Processor): ლოგიკური პროცესორი, რომელიც წარმოადგენს კონტექსტს ადგილობრივ რიგში გორუტინებისა და რესურსების, როგორიცაა მუტექსები და დაგეგმვა. P-ის რაოდენობა განსაზღვრულია ჩვეულებრივ პროცესორის ლოგიკური ბირთვების რაოდენობით, მაგრამ შეიძლება შეიცვალოს
GOMAXPROCS-ით. - M (Machine/Thread): ოპერაციული სისტემის ნაკადი, რომელიც ასრულებს კოდს. M დაკავშირებულია P-სთან და ასრულებს გორუტინებს ადგილობრივ ან გლობალურ რიგიდან.
სამუშაოს პრინციპი:
- ახალი გორუტინა შეიქმნება და მოთავსდება ხელმისაწვდომ P-ის ადგილობრივ რიგში.
- P-სთან დაკავშირებული M იღებს გორუტინას ადგილობრივ რიგიდან და ასრულებს მას.
- თუ ადგილობრივი რიგი ცარიელია, M ცდილობს გორუტინების მოპარვას სხვა P-ებიდან.
- თუ P ბლოკდება (მაგალითად, სისტემური ზარის დროს), დაკავშირებული M გამორთულია და სხვა M უკავშირდება ამ P-ს. ბლოკირებული M შეიძლება გამოყენებულ იქნას ხელახლა, როდესაც სისტემური ზარი დასრულდება.
- თუ გორუტინა ბლოკდება არხზე ან მუტექსზე, ის გადადის მოლოდინის რიგში, და დაგეგმვა ირჩევს სხვა გორუტინას იმავე M-ზე.
- დაგეგმვა პერიოდულად ახორციელებს შემოწმებებს, რათა დაარღვიოს ხანგრძლივი გორუტინების მუშაობა, და სხვა გორუტინებს მისცეს შესრულების დრო.
Preemption points (წინასწარი გაჩერების წერტილები) წარმოიქმნება გარკვეულ ადგილებში:
- ფუნქციების/მეთოდების გამოძახებები.
- ციკლები, რომლებიც შეიცავს ფუნქციის გამოძახებებს.
- მეხსიერების გამოყოფა.
- სინქრონიზაციის პრიმიტივები (არხები, მუტექსები).
მაგალითი დაგეგმვა:
package main
import (
"fmt"
"runtime"
"time"
)
func worker(id int) {
fmt.Printf("გორუტინა %d დაიწყო მუშაობა\n", id)
time.Sleep(time.Second) // მუშაობის იმიტაცია
fmt.Printf("გორუტინა %d დასრულდა მუშაობა\n", id)
}
func main() {
runtime.GOMAXPROCS(2) // ლოგიკური პროცესორების რაოდენობის განსაზღვრა
for i := 1; i <= 5; i++ {
go worker(i) // გორუტინების შექმნა
}
time.Sleep(3 * time.Second) // გორუტინების დასრულების მოლოდინი
fmt.Println("ძირითადი გორუტინა დასრულდა")
}
ამ მაგალითში 5 გორუტინა დაგეგმილია 2 ლოგიკურ პროცესორზე. Go-ის დაგეგმვა განაწილებს ამ გორუტინებს ოპერაციული სისტემის ხელმისაწვდომ ნაკადებზე, გამოიყენებს ადგილობრივ და შესაძლოა გლობალურ რიგებს, ასევე "მოპარვის" მექანიზმს.
უპირატესობები:
- მაშტაბურობა: ეფექტურად იყენებს მრავალბირთვიან სისტემებს.
- მცირე ხარჯები: მსუბუქი გორუტინები შედარებით ოპერაციული სისტემის ნაკადებთან.
- გამოყენების სიმარტივე: ინტეგრირებული დაგეგმვა ენის ფარგლებში, რომელიც ამარტივებს პარალელურ პროგრამირებას.