2014-09-04 15 views
21

Come sapere che un canale bufferizzato è pieno? Non so di essere bloccato quando il canale bufferizzato è pieno, invece ho scelto di rilasciare l'elemento inviato al canale bufferizzato.Golang - Come sapere che un canale bufferizzato è pieno

+0

La premessa della questione è che vuoi evitare che il canale si riempia mai. Ma il fatto che i canali * possano * sincronizzarsi e quindi bloccare entrambe le estremità è una parte importante del pensiero di CSP. Non tentare troppo di impedire che i buffer si riempiano fino a quando non si capisce la sincronizzazione in modo più completo. Come esercizio, prova a risolvere diversi problemi usando solo i canali * unbuffered *. Quindi puoi vedere come aggiungere il buffering in seguito può essere un miglioramento delle prestazioni di un sistema già funzionante. (Di tanto in tanto casi in cui troppo buffer può persino ridurre le prestazioni.) –

risposta

56

È possibile utilizzare select statement con un valore predefinito. Nel caso in cui non è possibile fare qualsiasi dei casi, come l'invio di un canale pieno, la dichiarazione farà il default:

package main 

import "fmt" 

func main() { 
    ch := make(chan int, 1) 

    // Fill it up 
    ch <- 1 

    select { 
    case ch <- 2: // Put 2 in the channel unless it is full 
    default: 
     fmt.Println("Channel full. Discarding value") 
    } 
} 

uscita:

canale pieno. Scartando valore

Playground:http://play.golang.org/p/1QOLbj2Kz2

controllare senza inviare

E 'anche possibile controllare il numero di elementi in coda in un canale utilizzando len(ch), come indicato nel Go specifications. Questo in combinazione con cap ci consente di verificare se un canale è pieno senza inviare alcun dato.

if len(ch) == cap(ch) { 
    // Channel was full, but might not be by now 
} else { 
    // Channel wasn't full, but might be by now 
} 

Si noti che il risultato del confronto potrebbe non essere valido nel momento in cui entra il blocco if

+0

Ma cosa succede se si vuole solo controllare se il buffer è pieno, non si scrive su di esso anche quando non lo è? C'è un modo per farlo? – Tom

+9

@Tom Si può effettivamente testare 'if len (ch) == cap (ch) {...}' dove 'len (ch)' è il numero di elementi nel canale e 'cap (ch)' è la capacità . Tuttavia, potrebbe non essere valido quando si inserisce il blocco if. – ANisus

+0

fantastico - ora questa è una risposta completa! – Tom

10

invece ho scelto di rilasciare l'elemento inviato al canale tamponata.

che si chiama "canale traboccante", e trovi la risposta di Anisus implementato in eapache/channels/overflowing_channel.go:

for elem := range ch.input { 
    // if we can't write it immediately, drop it and move on 
    select { 
    case ch.output <- elem: 
    default: 
    } 
} 
close(ch.output) 

Ma quel progetto eapache/channels implementa altre strategie così:

  • OverflowingChannel implementa il Channel interfaccia in un modo che non blocca mai lo scrittore.
    In particolare, se un valore è scritto su un OverflowingChannel quando il suo buffer è pieno
    (o, in un caso non bufferizzato, quando il destinatario non è pronto), quel valore viene semplicemente scartato.

Per il comportamento opposto (scartando l'elemento più vecchio, non l'ultima) vedere RingChannel.

+1

Una buona risposta. Canali traboccanti che perdono il più vecchio o il più nuovo oggetto possono a volte essere strumenti vitali nella vostra cassetta degli attrezzi. Considera un anello di goroutine: normalmente, qualsiasi loop (a.k.a * cycle *) causa un rischio di stallo. La modifica di uno dei canali per essere traboccante risolve questo problema. Perdere alcuni eventi non importa se sono il tipo che può diventare stantio può essere facilmente sostituito. Ci sono (ovviamente) altri modi per risolvere lo stesso problema di deadlock. –

1

Un altro esempio utile che ho trovato è stato this nifty implementation di Ring Buffer.

La citazione dalla fonte:

L'idea è semplice: collegare due canali bufferizzati i goroutine che inoltra i messaggi dal canale in ingresso al canale uscente . Ogni volta che non è possibile collocare un nuovo messaggio sul canale in uscita , estrarre un messaggio dal canale in uscita (che il è il messaggio più vecchio nel buffer), rilasciarlo e posizionare il nuovo messaggio nella nuova versione in uscita. canale.

Partenza this C version così ...

0

Usa len(channel) per verificare quanti elementi sono attualmente nel canale e che, per confrontare cap(channel), la sua capacità:

func TestChannelFull(t *testing.T) { 
    chansize := 100 
    ch := make(chan bool, chansize) 
    t.Log(cap(ch)) 
    t.Log(len(ch)) 
    for len(ch) < cap(ch) { 
     ch <- true 
    } 
    t.Log(len(ch)) 
    t.Log("quit because channel is full") 
} 

init_test.go:25: 100 
init_test.go:26: 0 
init_test.go:30: 100 
init_test.go:31: quit because channel is full 
PASS 
Problemi correlati