Middle
O que é o workshopping?
sobes.tech IA
Resposta da IA
O work stealing (roubo de trabalho) é um mecanismo de balanceamento de carga em sistemas multithread, onde um thread inativo "rouba" tarefas da fila de outro thread ocupado.
Características principais:
- Descentralizado: Cada thread gere sua própria fila de tarefas.
- Ativo: O thread inativo busca ativamente trabalho em outros.
- Alta performance: Eficaz para tarefas com carga desigual.
Em Golang, é usado no scheduler para distribuir goroutines entre os threads do sistema operacional (M). Quando um M termina seu trabalho atual (o pacote de goroutines em P), ele busca goroutines em outros P através de suas filas locais.
// Pseudocódigo demonstrando o conceito de work stealing
package main
import "fmt"
import "sync"
import "time"
type Task struct {
id int
}
// Simulação de fila de tarefas
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
}
// Tentativa de "roubar" tarefa de outra fila
func (q *TaskQueue) StealTask() (Task, bool) {
q.mu.Lock()
defer q.mu.Unlock()
if len(q.tasks) < 2 { // Não roubar se há poucas tarefas
return Task{}, false
}
// Roubar do meio ou do final para evitar conflitos com a extração local
index := len(q.tasks) / 2
task := q.tasks[index]
q.tasks = append(q.tasks[:index], q.tasks[index+1:]...)
return task, true
}
// Simulação de worker
func worker(id int, localQueue *TaskQueue, otherQueues []*TaskQueue, wg *sync.WaitGroup) {
defer wg.Done()
for {
// Tentar pegar tarefa da fila local
task, ok := localQueue.GetLocalTask()
if ok {
fmt.Printf("Worker %d executa tarefa %d localmente\n", id, task.id)
time.Sleep(100 * time.Millisecond) // Simulação de trabalho
continue
}
// Se a fila local estiver vazia, tentar roubar
stolen := false
for _, queue := range otherQueues {
if queue == localQueue {
continue // Não roubar de si mesmo
}
task, ok := queue.StealTask()
if ok {
fmt.Printf("Worker %d roubou tarefa %d\n", id, task.id)
time.Sleep(100 * time.Millisecond) // Simulação de trabalho
stolen = true
break // Sair após roubo bem-sucedido
}
}
if !stolen {
// Se não conseguiu roubar, talvez não haja mais tarefas
// Em um planejador real, haveria mecanismos mais complexos para terminar
// Para demonstração, sair após um tempo
fmt.Printf("Worker %d está inativo, esperando...\n", id)
time.Sleep(50 * time.Millisecond)
// Em cenário real, aqui haveria mecanismo de finalização ou parking
// return // Para demonstração, permitir sair
break // Para simplificar a simulação
}
}
}
func main() {
numWorkers := 4
numTasks := 20
taskQueues := make([]*TaskQueue, numWorkers)
for i := range taskQueues {
taskQueues[i] = &TaskQueue{}
}
// Distribuir tarefas de forma não uniforme para demonstração de work stealing
for i := 0; i < numTasks; i++ {
queueIndex := i % 2 // Mais tarefas nas primeiras duas filas
taskQueues[queueIndex].AddTask(Task{id: i})
}
var wg sync.WaitGroup
for i := 0; i < numWorkers; i++ {
wg.Add(1)
// Passar referências a todas as filas para o worker poder "roubar"
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("Todas as tarefas foram concluídas")
}