Sobes.tech
Middle

Kas ir workshopping?

sobes.tech AI

Atbilde no AI

Darba zagšana (work stealing) ir mehānisms slodzes līdzsvarošanai daudzšūnu sistēmās, kur neaktīvs pavediens (darbinieks) "zog" uzdevumus no cita, aizņemta pavediena rinda.

Galvenās īpašības:

  • Dezentralizēts: Katrs pavediens pārvalda savu vietējo uzdevumu rindu.
  • Aktīvs: Neaktīvs pavediens aktīvi meklē darbu citos.
  • Augsta veiktspēja: Efektīvs nevienmērīgas slodzes gadījumā.

Golang tiek izmantots plānotājā (scheduler) goroutine-ļu sadalīšanai starp operētājsistēmas pavedieniem (M). Kad viens M pabeidz savu pašreizējo darbu (goroutine kopu P-de), tas meklē goroutine-ļ citos P caur to vietējām rindām.

// Pseido-kods, kas demonstrē darba zagšanas koncepciju
package main

import "fmt"
import "sync"
import "time"

type Task struct {
	id int
}

// Uzdevumu rinda
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
}

// Mēģinājums "zagt" uzdevumu no citas rindas
func (q *TaskQueue) StealTask() (Task, bool) {
	q.mu.Lock()
	defer q.mu.Unlock()
	if len(q.tasks) < 2 { // Nezog, ja ir maz uzdevumu
		return Task{}, false
	}
	// Zog no vidus vai beigām, lai izvairītos no konfliktiem ar vietējo izņemšanu
	index := len(q.tasks) / 2
	task := q.tasks[index]
	q.tasks = append(q.tasks[:index], q.tasks[index+1:]...)
	return task, true
}

// Darba pavediena funkcija
func worker(id int, localQueue *TaskQueue, otherQueues []*TaskQueue, wg *sync.WaitGroup) {
	defer wg.Done()

	for {
		// Mēģinājums ņemt uzdevumu no vietējās rindas
		task, ok := localQueue.GetLocalTask()
		if ok {
			fmt.Printf("Darbinieks %d veic uzdevumu %d vietēji\n", id, task.id)
			time.Sleep(100 * time.Millisecond) // Imitācija
			continue
		}

		// Ja vietējā rinda ir tukša, mēģinām zagšanu
		stolen := false
		for _, queue := range otherQueues {
			if queue == localQueue {
				continue // Nezog no sevis
			}
			task, ok := queue.StealTask()
			if ok {
				fmt.Printf("Darbinieks %d zag uzdevumu %d\n", id, task.id)
				time.Sleep(100 * time.Millisecond) // Imitācija
				stolen = true
				break // Veiksmīgi zagts, iziet
			}
		}

		if !stolen {
			// Ja neizdevās zagšanu, varbūt uzdevumi vairs nav
			// Reāla plānotājā ir sarežģītāki mehānismi
			// Demostrācijā vienkārši iziet pēc noteikta laika
			fmt.Printf("Darbinieks %d gaida...\n", id)
			time.Sleep(50 * time.Millisecond)
			// Reāla scenārijā šeit būtu beigu vai parkošanas mehānisms
			// return // Demostrācijā ļausim iziet
			break // Vienkāršības pēc
		}
	}
}

func main() {
	numWorkers := 4
	numTasks := 20
	taskQueues := make([]*TaskQueue, numWorkers)
	for i := range taskQueues {
		taskQueues[i] = &TaskQueue{}
	}

	// Nepareiza uzdevumu sadale demonstrācijai
	for i := 0; i < numTasks; i++ {
		queueIndex := i % 2 // Pirmās divas darba vietas ar vairāk uzdevumiem
		taskQueues[queueIndex].AddTask(Task{id: i})
	}

	var wg sync.WaitGroup
	for i := 0; i < numWorkers; i++ {
		wg.Add(1)
		// Nododam atsauces uz visas rindas, lai darbinieks varētu "zagt"
		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("Visas uzdevumi izpildīti")
}