Middle
Cos'è il workshopping?
sobes.tech AI
Risposta dell'AI
Lo work stealing (furto di lavoro) è un meccanismo di bilanciamento del carico nei sistemi multithread, in cui un thread inattivo "ruba" compiti dalla coda di un altro thread occupato.
Caratteristiche principali:
- Decentralizzato: Ogni thread gestisce la propria coda di compiti.
- Attivo: Il thread inattivo cerca attivamente lavoro dagli altri.
- Alta efficienza: È efficace per compiti con carichi disomogenei.
In Golang, viene usato nello scheduler per distribuire le goroutine tra i thread del sistema operativo (M). Quando un M termina il suo lavoro corrente (il pacchetto di goroutine su P), cerca goroutine negli altri P attraverso le loro code locali.
// Pseudocodice che dimostra il concetto di work stealing
package main
import "fmt"
import "sync"
import "time"
type Task struct {
id int
}
// Simulazione di una coda di compiti
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
}
// Tentativo di "furto" di un compito da un'altra coda
func (q *TaskQueue) StealTask() (Task, bool) {
q.mu.Lock()
defer q.mu.Unlock()
if len(q.tasks) < 2 { // Non "rubiamo" se ci sono pochi compiti
return Task{}, false
}
// Rubiamo dalla metà o dalla fine, per evitare conflitti con l'estrazione locale
index := len(q.tasks) / 2
task := q.tasks[index]
q.tasks = append(q.tasks[:index], q.tasks[index+1:]...)
return task, true
}
// Simulazione di un worker
func worker(id int, localQueue *TaskQueue, otherQueues []*TaskQueue, wg *sync.WaitGroup) {
defer wg.Done()
for {
// Tentare di prendere un compito dalla coda locale
task, ok := localQueue.GetLocalTask()
if ok {
fmt.Printf("Worker %d esegue il compito %d localmente\n", id, task.id)
time.Sleep(100 * time.Millisecond) // Simulazione di lavoro
continue
}
// Se la coda locale è vuota, tentiamo di rubare
stolen := false
for _, queue := range otherQueues {
if queue == localQueue {
continue // Non rubare da sé
}
task, ok := queue.StealTask()
if ok {
fmt.Printf("Worker %d ha rubato il compito %d\n", id, task.id)
time.Sleep(100 * time.Millisecond) // Simulazione di lavoro
stolen = true
break // Uscire dopo un furto riuscito
}
}
if !stolen {
// Se non si è riusciti a rubare, forse non ci sono più compiti
// In uno scheduler reale, ci sarebbero meccanismi più complessi per terminare
// Per la demo, uscire dopo un po'
fmt.Printf("Worker %d è inattivo, in attesa...\n", id)
time.Sleep(50 * time.Millisecond)
// In uno scenario reale, qui ci sarebbe un meccanismo di terminazione o parking
// return // Per la demo, permettiamo di uscire
break // Per semplificare la simulazione
}
}
}
func main() {
numWorkers := 4
numTasks := 20
taskQueues := make([]*TaskQueue, numWorkers)
for i := range taskQueues {
taskQueues[i] = &TaskQueue{}
}
// Distribuire i compiti in modo non uniforme per dimostrare il work stealing
for i := 0; i < numTasks; i++ {
queueIndex := i % 2 // Più compiti nei primi due worker
taskQueues[queueIndex].AddTask(Task{id: i})
}
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
// Passare i riferimenti a tutte le code, così il worker può "rubare"
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("Tutti i compiti sono stati completati")
}
```}}}}}]}]}}}]},