Sobes.tech
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-სთან და ასრულებს გორუტინებს ადგილობრივ ან გლობალურ რიგიდან.

სამუშაოს პრინციპი:

  1. ახალი გორუტინა შეიქმნება და მოთავსდება ხელმისაწვდომ P-ის ადგილობრივ რიგში.
  2. P-სთან დაკავშირებული M იღებს გორუტინას ადგილობრივ რიგიდან და ასრულებს მას.
  3. თუ ადგილობრივი რიგი ცარიელია, M ცდილობს გორუტინების მოპარვას სხვა P-ებიდან.
  4. თუ P ბლოკდება (მაგალითად, სისტემური ზარის დროს), დაკავშირებული M გამორთულია და სხვა M უკავშირდება ამ P-ს. ბლოკირებული M შეიძლება გამოყენებულ იქნას ხელახლა, როდესაც სისტემური ზარი დასრულდება.
  5. თუ გორუტინა ბლოკდება არხზე ან მუტექსზე, ის გადადის მოლოდინის რიგში, და დაგეგმვა ირჩევს სხვა გორუტინას იმავე M-ზე.
  6. დაგეგმვა პერიოდულად ახორციელებს შემოწმებებს, რათა დაარღვიოს ხანგრძლივი გორუტინების მუშაობა, და სხვა გორუტინებს მისცეს შესრულების დრო.

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-ის დაგეგმვა განაწილებს ამ გორუტინებს ოპერაციული სისტემის ხელმისაწვდომ ნაკადებზე, გამოიყენებს ადგილობრივ და შესაძლოა გლობალურ რიგებს, ასევე "მოპარვის" მექანიზმს.

უპირატესობები:

  • მაშტაბურობა: ეფექტურად იყენებს მრავალბირთვიან სისტემებს.
  • მცირე ხარჯები: მსუბუქი გორუტინები შედარებით ოპერაციული სისტემის ნაკადებთან.
  • გამოყენების სიმარტივე: ინტეგრირებული დაგეგმვა ენის ფარგლებში, რომელიც ამარტივებს პარალელურ პროგრამირებას.