Middle
რა არის ვორქშოპინგი?
sobes.tech AI
პასუხი AI-სგან
სამუშაოს მოპარვა (work stealing) არის მექანიზმი დატვირთვის ბალანსისთვის მრავალთრაქტულ სისტემებში, სადაც უქმნელი ნაკადი (შრომა) "მპარავს" დავალებებს სხვა, დაკავებულ ნაკადიდან.
ძირითადი მახასიათებლები:
- დეცენტრალიზებული: თითოეული ნაკადი მართავს თავის ადგილობრივ დავალებების რიგს.
- აქტიური: უქმნელი ნაკადი აქტიურად ეძებს სამუშაოს სხვა ნაკადებიდან.
- მაღალი ეფექტურობა: ეფექტურია არათანაბარ დატვირთვაზე.
Golang-ში ეს გამოიყენება სქედულერში (scheduler) goroutine-ების განაწილებისთვის ოპერაციული სისტემის ნაკადებზე (M). როდესაც ერთი M ამთავრებს მიმდინარე სამუშაოს (goroutine-ების პაკეტი P-ზე), ის ეძებს goroutine-ებს სხვა P-ების ადგილობრივ რიგებში.
// ფსევდოკოდი, რომელიც აჩვენებს სამუშაოს მოპარვის კონცეფციას
package main
import "fmt"
import "sync"
import "time"
type Task struct {
id int
}
// სამუშაოების რიგი
type TaskQueue struct {
tasks []Task
mu sync.Mutex
}
func (q *TaskQueue) AddTask(task Task) {
q.mu.Lock()
defer q.mu.Unlock()
q.tasks = append(q.tasks, task)
}
func (q *TaskQueue) GetLocalTask() (Task, bool) {
q.mu.Lock()
defer q.mu.Unlock()
if len(q.tasks) == 0 {
return Task{}, false
}
task := q.tasks[0]
q.tasks = q.tasks[1:]
return task, true
}
// მცდელობა "მიპაროს" სხვა რიგიდან
func (q *TaskQueue) StealTask() (Task, bool) {
q.mu.Lock()
defer q.mu.Unlock()
if len(q.tasks) < 2 { // არ ვმიპარავთ, თუ ცოტა დავალებებია
return Task{}, false
}
// ვმიპარავთ შუა ან ბოლოს, რათა არ დავრღვათ ადგილობრივი ამოღება
index := len(q.tasks) / 2
task := q.tasks[index]
q.tasks = append(q.tasks[:index], q.tasks[index+1:]...)
return task, true
}
// სამუშაო ნაკადი
func worker(id int, localQueue *TaskQueue, otherQueues []*TaskQueue, wg *sync.WaitGroup) {
defer wg.Done()
for {
// მცდელობა მიიღოს დავალება ადგილობრივ რიგიდან
task, ok := localQueue.GetLocalTask()
if ok {
fmt.Printf("შრომა %d ახორციელებს დავალებას %d ადგილობრივად\n", id, task.id)
time.Sleep(100 * time.Millisecond) // იმიტაცია
continue
}
// თუ ადგილობრივი რიგი ცარიელია, მცდელობა მიპაროს
stolen := false
for _, queue := range otherQueues {
if queue == localQueue {
continue // საკუთარი რიგიდან არ ვმიპარავთ
}
task, ok := queue.StealTask()
if ok {
fmt.Printf("შრომა %d მიპარავს დავალებას %d\n", id, task.id)
time.Sleep(100 * time.Millisecond) // იმიტაცია
stolen = true
break // წარმატებით მიპაროს, გამოვდივართ
}
}
if !stolen {
// თუ ვერ მიპარავს, შეიძლება დავალებები აღარ იყოს
// რეალურ სქედულერში უფრო რთული მექანიზმები არსებობს
// დემონსტრაციისთვის უბრალოდ გამოვდივართ გარკვეული დროის შემდეგ
fmt.Printf("შრომა %d ელოდება...\n", id)
time.Sleep(50 * time.Millisecond)
// რეალურ სცენარში აქ იქნებოდა დასრულების ან პარკირების მექანიზმი
// return // დემონსტრაციისთვის გამოვდივართ
break // სიმულაციის გამარტივებისთვის
}
}
}
func main() {
numWorkers := 4
numTasks := 20
taskQueues := make([]*TaskQueue, numWorkers)
for i := range taskQueues {
taskQueues[i] = &TaskQueue{}
}
// დავალებების განაწილება არათანაბრად მიპაროსთვის
for i := 0; i < numTasks; i++ {
queueIndex := i % 2 // პირველი ორი სამუშაოსთვის მეტი დავალება
taskQueues[queueIndex].AddTask(Task{id: i})
}
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
// ყველა რიგის მითითება, რათა სამუშაოს შეუძლია "მიპაროს"
otherQueues := make([]*TaskQueue, 0, numWorkers-1)
for j := range taskQueues {
if i != j {
otherQueues = append(otherQueues, taskQueues[j])
}
}
go worker(i, taskQueues[i], otherQueues, &wg)
}
wg.Wait()
fmt.Println("ყველა დავალება შესრულდა")
}