Middle
Šta je workshopping?
sobes.tech АИ
Одговор од АИ
Механизам за крадене на работа (work stealing) је механизам за балансирање оптерећења у системима са више нитова, где неактивна нит "краде" задатке из реда друге заузете нитове.
Кључне карактеристике:
- Децентрализовано: Свако нит управља својим локалним редом задатака.
- Активно: Неактивна нит активно тражи посао од других.
- Висока перформанса: Ефективан за задатке са неравномерним оптерећењем.
У Golang-у се користи у планеру (scheduler) за расподелу горутина по системским нитовима (M). Када M заврши свој тренутни посао (пакет горутина на P), тражи горутине у другим P преко њихових локалних редова.
// Псеудо-кад који демонстрира концепт work stealing
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{}
}
// Неправилна расподела задатака за демонстрацију work stealing
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("Све задатке су извршене")
}