Sobes.tech
Middle

Ի՞նչ է վարպետության աշխատանքը։

sobes.tech AI

Պատասխան AI-ից

Աշխատանքի գողություն (work stealing) մեխանիզմ է, որը հավասարակշռում է բեռը բազմատողային համակարգերում, որտեղ անգործ տողը (աշխատողը) "գողանում" է խնդիրներ մյուս, զբաղված տողից:

Հիմնական հատկանիշներ:

  • Դեցենտրալիզացված: Յուրաքանչյուր տող կառավարում է իր տեղական խնդիրների հերթը:
  • Ակտիվ: Անգործ տողը ակտիվորեն փնտրում է աշխատանք այլ տողերից:
  • Բարձր արդյունավետություն: Էֆեկտիվ է անհավասար բեռների դեպքում:

Golango- ում օգտագործվում է պլանավորիչում (scheduler)՝ goroutin-ների բաշխման համար օպերացիոն համակարգի (M) տողերի միջև:

// Պսևդոկոդ, որը ցույց է տալիս աշխատանքային գողության կոնցեպցիան
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("Բոլոր խնդիրները կատարվեցին")
}