Tag: go, golang, backend, concurrency, goroutines, channels
Livello: intermedio
TL;DR: Prontuario sintetico su Go per colloqui tecnici: goroutine, channel, gestione errori esplicita, struct e interfacce implicite.
Compilato, tipizzato staticamente, con garbage collection e sintassi essenziale.
Concorrenza nativa basata su CSP tramite goroutine e channel.
Le goroutine hanno stack iniziali leggeri (pochi KB) e crescono dinamicamente.
Channel non bufferizzati sincronizzano lettura e scrittura in modo bloccante.
Channel bufferizzati consentono invii non bloccanti fino a capienza massima.
Assenza di classi ed ereditarieta: polimorfismo gestito tramite composition e interface.
Implementazione delle interfacce strutturale e implicita: zero keyword implements.
Gestione errori esplicita: le funzioni restituiscono valori di tipo error.
Zero value assegnato automaticamente a ogni variabile non inizializzata.
Istruzione defer esegue una funzione all'uscita dal blocco circostante.
Q: Qual e la differenza tra goroutine e thread di sistema?R: La goroutine e gestita dal runtime di Go con scheduling M:N su thread OS. Richiede circa 2 KB di memoria iniziale contro i megabyte del thread di sistema.
Q: Come gestisci la sincronizzazione tra piu goroutine?R: Uso sync.WaitGroup per attendere completamenti multipli, oppure channel per lo scambio di messaggi e context.Context per propagare annullamenti e timeout.
Q: Come funziona il polimorfismo in Go senza ereditarieta?R: Definisco un'interfaccia con i metodi necessari. Qualsiasi struct che implementa tali firme soddisfa automaticamente l'interfaccia senza dichiarazioni esplicite.
Q: Quando conviene usare un puntatore receiver rispetto a un value receiver?R: Uso un puntatore receiver se il metodo deve mutare lo stato della struct, oppure per evitare copie costose di struct pesanti in memoria.
Q: Come si gestiscono i panic in produzione?R: Riservo panic per errori irrecuperabili all'avvio. Per isolare blocchi critici uso recover() all'interno di una funzione differita tramite defer.
Goroutine leak causato da channel mai letti o mai chiusi.
Data race su variabili condivise senza sincronizzazione con mutex o channel.
Invio su channel chiuso genera un panic immediato a runtime.
Chiusura di un channel gia chiuso scatena un panic.
Scrittura o lettura su un channel nil blocca la goroutine indefinitamente.
Modifica della variabile di iterazione in closure concorrenti senza ridefinizione locale.
Uso improprio di defer all'interno di loop lunghi: posticipa il rilascio risorse.
Confusione tra slice e array: lo slice e una vista su array sottostante.
// Esempio worker con sync.WaitGroup e channel
package main
import (
"context"
"fmt"
"sync"
"time"
)
func worker(ctx context.Context, id int, jobs <-chan int, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <-ctx.Done():
return
case job, ok := <-jobs:
if !ok {
return
}
fmt.Printf("Worker %d: job %d\n", id, job)
}
}
}
Bash
# Esecuzione test con race detector
go test -race ./...
# Verifica e pulizia delle dipendenze del modulo
go mod tidy
# Compilazione binario statico
CGO_ENABLED=0 GOOS=linux go build -o app ./main.go
[ ] Meccaniche del memory model e race detector di Go
[ ] Differenze tra sync.Mutex e sync.RWMutex
[ ] Pattern di cancellazione con context.WithTimeout
[ ] Anatomia interna di slice, map e channel
[x] Sintassi e utilizzo dell'istruzione defer
[x] Dichiarazione di struct e metodi con receiver