Condivisione della tecnologia

Linguaggio Go: canale di programmazione simultaneo (doppio canale, canale singolo) ed esempi di applicazioni (produttore consumatore, modello di stampante)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Canale

Le goroutine vengono eseguite nello stesso spazio di indirizzi, quindi l'accesso alla memoria condivisa deve essere sincronizzato.Le goroutine condividono la memoria attraverso la comunicazione, piuttosto che condividendo la memoria.

Il canale del tipo di riferimento è un'implementazione specifica della modalità CSP e viene utilizzato per la comunicazione con più goroutine. Implementa la sincronizzazione internamente per garantire la sicurezza della concorrenza.

tipo di canale

Come map, anche channel è un riferimento alla struttura dati sottostante creata da make.
Quando copiamo un canale o lo utilizziamo per il passaggio di parametri di funzione, copiamo semplicemente un riferimento al canale, in modo che il chiamante e il chiamato facciano riferimento allo stesso oggetto canale. Come altri tipi di riferimento, il valore zero del canale è nil.
Quando definisci un canale, devi anche definire il tipo di valori inviati al canale. I canali possono essere creati utilizzando la funzione make() incorporata:

make (chan Type)
make (chan Type,capacity)
  • 1
  • 2
  • Quando capacità=0, il canale non è bufferizzato e blocca la lettura e la scrittura. Quando capacità>0, il canale ha una cache e la scrittura non verrà bloccata finché gli elementi di capacità non saranno riempiti.
  • Chamnel riceve e invia dati tramite l'operatore <-. La sintassi per l'invio e la ricezione dei dati è:
channel<-value   //发送value 到channel
<-channel     //接收并将其丢弃
x:=<-channel   //从 channel 中接收数据,并賦偵给x
x,ok:=<-channel   //功能同上,同时检查通道是否已关闭或者是否为空
  • 1
  • 2
  • 3
  • 4

Per impostazione predefinita, il canale si blocca durante la ricezione e l'invio di dati, a meno che l'altra estremità non sia pronta, rendendo la sincronizzazione della goroutine più semplice senza la necessità di blocchi espliciti.

compiere

Inserisci qui la descrizione dell'immagine

  • I tubi sono posizionati uno davanti alla stampa e uno sul retro della stampa.
  • Se Person2 viene eseguito per primo e non sono presenti dati nella pipeline, si bloccherà.
  • Persona1 esegue e dopo aver stampato i dati, immette i dati nella pipe, Persona2 li rileverà e inizierà a stampare.

Implementare la sincronizzazione e l'interazione dei dati attraverso i canali

Inserisci qui la descrizione dell'immagine
Inserisci qui la descrizione dell'immagine

canale senza buffer

Un canale senza buffer è un canale che non ha la capacità di salvare alcun valore prima di ricevere.
Questo tipo di canale richiede che la goroutine di invio e quella di ricezione siano pronte contemporaneamente a completare le operazioni di invio e ricezione. Se due goroutine non sono pronte contemporaneamente, il canale farà sì che la goroutine che esegue per prima l'operazione di invio o ricezione si blocchi e attenda.
Questa interazione di invio e ricezione sul canale è intrinsecamente sincrona. Nessuna operazione può esistere indipendentemente dall’altra.
La figura seguente mostra come due goroutine possono condividere un valore utilizzando canali senza buffer:
Inserisci qui la descrizione dell'immagine

  • Vale a dire, il canale stesso non può memorizzare cose. Se inserisci qualcosa in uno, puoi rimuoverlo immediatamente.
  • Nel passaggio 1, entrambe le goroutine arrivano sul canale, ma nessuna delle due inizia a inviare o ricevere.
  • Nel passaggio 2, la goroutine a sinistra mette la mano nel canale, simulando l'atto di inviare dati al canale. In questo momento, la goroutine sarà bloccata nel canale fino al completamento dello scambio.
  • Nel passaggio 3, la goroutine a destra mette la mano nel canale, simulando la ricezione dei dati dal canale. Anche questa goroutine sarà bloccata nel canale fino al completamento dello scambio.
  • Nei passaggi 4 e 5 avviene uno scambio e infine, nel passaggio 6, entrambe le goroutine tolgono le mani dal canale, simulando il rilascio della goroutine bloccata. Entrambe le goroutine ora possono fare altre cose.

Crea un canale senza buffer

make (chan Type)//等价于make (chan Type,0)
  • 1

Se non viene specificata la dimensione del buffer, il canale è sincrono e pertanto si blocca finché il mittente non è pronto a inviare e il destinatario è pronto a ricevere.

Inserisci qui la descrizione dell'immagine

  • ch&lt;-i scrive i dati sul canale e la coroutine principale non verrà eseguita finché non lo rileva. In questo momento, anche la sub-coroutine si bloccherà e attenderà. Dopo che la pipe avrà letto i dati, la sub-coroutine continuerà .
  • Tuttavia, println stampa i dati dopo che la pipe è stata letta, quindi la velocità di stampa è determinata dal sistema.

canale bufferizzato

Un canale bufferizzato è un canale che può memorizzare uno o più valori prima di essere ricevuto.
Questo tipo di canale non richiede che la goroutine debba completare l'invio e la ricezione allo stesso tempo. I canali avranno anche condizioni diverse che bloccano le azioni di invio e ricezione. L'azione di ricezione verrà bloccata solo se nel canale non è presente alcun valore da ricevere. L'azione di invio si bloccherà solo se il canale non ha buffer disponibile per accogliere il valore inviato.
Ciò porta a una grande differenza tra canali con buffer e senza buffer: i canali senza buffer garantiscono che le goroutine di invio e ricezione scambieranno dati allo stesso tempo: i canali con buffer non hanno tale garanzia.
Inserisci qui la descrizione dell'immagine

  • Nel passaggio 1, la goroutine a destra riceve un valore dal canale.
  • Nel passaggio 2, la goroutine a destra completa l'azione di ricezione del valore in modo indipendente, mentre la goroutine a sinistra invia un nuovo valore al canale.
  • Nel passaggio 3, la goroutine a sinistra sta ancora inviando un nuovo valore al canale, mentre la goroutine a destra sta ricevendo un altro valore dal canale. Le due operazioni in questo passaggio non sono né sincronizzate né bloccate a vicenda.
  • Infine, nel passaggio 4, tutte le operazioni di invio e ricezione sono state completate e ci sono ancora alcuni valori nel canale e spazio per memorizzare altri valori.

creare

make (chan Type,capacity)
  • 1

Se viene fornita una capacità buffer, il canale è asincrono. Finché il buffer ha spazio inutilizzato per l'invio di dati o contiene dati che possono essere ricevuti, la comunicazione procede senza blocchi.

compiere

Inserisci qui la descrizione dell'immagine

  • Se ne vengono memorizzati abbastanza tre, si bloccherà e attenderà la lettura. Quando la coroutine principale avrà letto un dato e c'è spazio libero, la coroutine secondaria continuerà l'esecuzione e il successivo ordine di stampa non sarà certo.
    Inserisci qui la descrizione dell'immagine

chiudere il canale

Il canale può utilizzare ok per rilevare se il canale è ancora aperto. Se il canale è chiuso, i dati non verranno letti.

Inserisci qui la descrizione dell'immagine

  • num ,ok:=<-chPuò rilevare se un canale è chiuso.

Avviso

  • I canali non hanno bisogno di essere chiusi così spesso come i file. Chiudi il canale solo quando non hai davvero dati da inviare, o se vuoi terminare esplicitamente il range loop o simili;
  • Dopo aver chiuso il canale, non è più possibile inviare dati al canale (viene attivato un errore di panico, che fa sì che la ricezione restituisca immediatamente un valore zero):
  • Dopo aver chiuso il canale, puoi continuare a ricevere dati dal canale:
  • Per nessun canale, sia l'invio che la ricezione verranno bloccati.
    Inserisci qui la descrizione dell'immagine
  • È possibile utilizzare la portata per attraversare il canale e uscire automaticamente dal loop
    Inserisci qui la descrizione dell'immagine

Canale unidirezionale

Per impostazione predefinita, il canale è a doppia domanda, ovvero è possibile inviargli dati e riceverli.
Tuttavia, spesso vediamo un canale passato come parametro e si prevede che il valore venga utilizzato in una direzione, solo per inviare dati o solo per ricevere dati. A questo punto, possiamo specificare la direzione del canale. La dichiarazione di una variabile chamel unidirezionale è molto semplice, come segue:

var ch1 chan int //ch1双向
var ch2 chan<-float64 //ch2单向,只能用于写float64数据
var ch3 <-chan int  //ch3单向,只能用于读int数据
  • 1
  • 2
  • 3

chan&lt;- significa che i dati entrano nella pipe e vengono scritti nella pipe, che viene inviata al chiamante.
&lt;-chan indica che i dati escono dalla pipe. Per il chiamante, vengono ottenuti i dati dalla pipe, che ovviamente è l'input.
Un canale può essere implicitamente convertito in una coda unidirezionale, solo in ricezione o solo in invio, ma un canale unidirezionale non può essere convertito in un canale normale;

Inserisci qui la descrizione dell'immagine

Utilizzare un unico canale per implementare il modello produttore-consumatore

Inserisci qui la descrizione dell'immagine