Riepilogo: worker pool
Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 66 di 107.
Sfida
FacileCostruiamo un sistema di elaborazione delle attività usando il modello del pool di worker! Creerai un pool di worker che elaborano attività computazionali in parallelo, mostrando come canali, goroutine e WaitGroups lavorino insieme per gestire i carichi di lavoro in modo efficiente.
Organizzerai il codice in due file:
pool.go: definisci i componenti e la logica del pool di worker.Crea una struct
Taskcon i campiID(int) eValue(int), che rappresentano il lavoro da svolgere.Crea una struct
Resultcon i campiTaskID(int) eComputed(int), che rappresentano il lavoro completato.Implementa una funzione
Workerche accetta un ID worker (int), un canale di attività di sola ricezione (<-chan Task), un canale di risultati di solo invio (chan<- Result) e un puntatore a unsync.WaitGroup. Ogni worker deve:- Usare
defer wg.Done()per segnalare il completamento - Iterare sul canale delle attività per elaborare ogni attività
- Calcolare, per ogni attività, il quadrato del suo Value
- Inviare un Result con il TaskID e il quadrato calcolato
Implementa una funzione
RunPoolche accetta il numero di worker (int) e una slice di attività. Questa funzione deve:- Creare canali con buffer per le attività e i risultati (usa il numero di attività come dimensione del buffer)
- Avviare il numero specificato di worker come goroutine
- Inviare tutte le attività al canale delle attività, quindi chiuderlo
- Usare una goroutine con il WaitGroup per chiudere il canale dei risultati dopo che tutti i worker hanno terminato
- Raccogliere tutti i risultati in una slice e restituirla
- Usare
main.go: leggi l'input e coordina il pool di worker.Leggi il numero di worker, poi il numero di attività. Per ogni attività, leggi il suo ID e il suo valore. Chiama
RunPoolcon i worker e le attività, quindi stampa ogni risultato nel formato:Task [TaskID]: [Computed]Stampa i risultati ordinati per TaskID in ordine crescente.
Verranno forniti i seguenti input:
- Riga 1: numero di worker (intero)
- Riga 2: numero di attività (intero)
- Righe successive: per ogni attività, due righe: l'ID dell'attività (intero), poi il suo valore (intero)
Per esempio, dato:
2
4
1
3
2
5
3
2
4
7L'output dovrebbe essere:
Task 1: 9
Task 2: 25
Task 3: 4
Task 4: 49I worker elaborano le attività in parallelo (3 al quadrato è 9, 5 al quadrato è 25, ecc.) e i risultati vengono raccolti e visualizzati in ordine di TaskID. Con 2 worker che gestiscono 4 attività, il lavoro viene distribuito in modo efficiente nel pool.
Provalo tu
package main
import (
"fmt"
"sort"
)
func main() {
// Leggi il numero di lavoratori
var numWorkers int
fmt.Scanln(&numWorkers)
// Leggi il numero di attività
var numTasks int
fmt.Scanln(&numTasks)
// Leggi le attività
tasks := make([]Task, numTasks)
for i := 0; i < numTasks; i++ {
var id, value int
fmt.Scanln(&id)
fmt.Scanln(&value)
tasks[i] = Task{ID: id, Value: value}
}
// TODO: Chiama RunPool con i lavoratori e le attività
// TODO: Ordina i risultati per TaskID in ordine crescente
// TODO: Stampa ogni risultato nel formato: Task [TaskID]: [Computed]
}
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online