Programmazione sincronizzata/asincrona

Dec 06 2022
In questo blog, cerco di spiegare al meglio cos'è la programmazione sincrona e asincrona.
Se un chiamante invia un messaggio a un destinatario e attende una risposta, può fare qualcos'altro? Sto dicendo un chiamante/ricevitore invece di un server/client, poiché queste chiamate avvengono ovunque. In base alla progettazione, quando abbiamo creato i computer per la prima volta, tutto era sincrono.
Sincronizzato/Asincrono

Se un chiamante invia un messaggio a un destinatario e attende una risposta, può fare qualcos'altro? Sto dicendo un chiamante/ricevitore invece di un server/client, poiché queste chiamate avvengono ovunque. In base alla progettazione, quando abbiamo creato i computer per la prima volta, tutto era sincrono. Questo può essere visto come un'onda sinusoidale in cui un chiamante e un server sono sincronizzati o hanno lo stesso ritmo. Asincrono significa che non sono sincronizzati.

I/O sincrono
Il chiamante invia la richiesta al ricevente e poi la blocca. Questo è quando il chiamante è bloccato, non fa nulla, e quindi una totale perdita di tempo. In passato, la CPU rimuoveva il processo dal processore, pensando che fosse semplicemente bloccato, e aggiungeva un nuovo processo non bloccato (cambio di contesto). Quindi il chiamante non può essere eseguito nel frattempo. Infine, quando il ricevitore risponde, il sistema operativo può riportare il processo al processore. Quindi il chiamante sblocca. Questo mostra come il client e il server sono completamente sincronizzati.

Esempio di I/O sincrono del sistema operativo:
1. Il programma chiede alla CPU di leggere un file dal disco.
2. Il thread principale del programma viene rimosso dalla CPU.
3. La lettura viene completata e il programma riprende l'esecuzione.

// A simple Js example
// Program starts
// Programs uses CPU to execute work.

SampleFunction();
// Program reads from the disk
// Program can't do anything until file loads
readfile("SyncExample.dat")

// Program resumes

Parlando specificamente dal punto di NodeJs, utilizza epoll in Linux e, nel caso di Windows, utilizza uno stack completista. Un'altra cosa che fa nodeJs è avviare un nuovo thread che si blocca nel caso in cui qualcosa richieda di eseguire un'operazione di blocco. Per impostazione predefinita, Nodejs ha 4 thread di lavoro nella libreria libuv , che utilizza per le operazioni di I/O, ma è configurabile.

Esempio di una chiamata asincrona del sistema operativo (NodeJS)
a) Il programma avvia un thread secondario
b) Il thread secondario legge dal disco. Ovviamente, il sistema operativo rimuove dal processore corrente
c) il programma principale viene ancora eseguito ed eseguito.
d) Il thread termina e chiama il thread principale.

// A simple Javascript example
// Program starts
// Programs uses CPU to execute work.

SampleFunction();
// Program reads from the disk
// Program hapilly moves on to the samplefunction2
readfile("SyncExample.dat", onReadFinish(console.log))
//file is not probably read yet

SampleFunction2();
//onReadFinish function called
// executing it

Ora discuteremo le cose esclusivamente dal punto di vista di un client e di un server (backend).
Quindi la sincronicità può anche essere conosciuta come una proprietà del client in cui può aspettare o andare avanti. Al giorno d'oggi, nessun client è sincrono e la maggior parte delle librerie è asincrona. Principalmente il client invia la richiesta e riceve una risposta e alcuni callback vengono chiamati ogni volta che la risposta viene eseguita. Quindi in Node.js in particolare, c'è un ciclo principale del ciclo di eventi che verifica la risposta.
Questo può creare confusione, come lo è sempre stato per me quando qualcuno lo ha spiegato con questo bellissimo esempio di vita reale che sincrono è come fare una domanda in una riunione in cui la riunione andrà avanti se il presentatore risponde. Asincrono è come porre una domanda in un'e-mail a cui è possibile rispondere ogni volta che il destinatario ha tempo.

Elaborazione back-end asincrona

Essendo un ingegnere di backend, sarebbe ingiusto se non parlassi di rendere il backend asincrono. In molti repository di codice, il client è principalmente asincrono, ma il back-end lo fa comunque attendere. Quindi, quando un client richiede di inviare alcuni dati, diciamo per eseguire una chiamata al database, molte volte viene chiesto di attendere la risposta dal back-end, che restituisce una risposta di stato 200 dopo il commit. Ora, se spostiamo l'obiettivo al back-end, il front-end è asincrono, ma il back-end è ancora sincrono. Quindi, come restituiamo una risposta immediata?
Una delle soluzioni è utilizzare questa bellissima struttura di dati, chiamata coda. Se un client invia una richiesta, non promettiamo di eseguirla immediatamente, ma la scarichiamo nella coda. La ragione di ciò è che il backend potrebbe completare le richieste precedenti e il client non è più bloccato; possiamo inviare la risposta che abbiamo messo in coda la richiesta, ed ecco una promessa/JobID. Per ulteriori informazioni, puoi leggere sulle code dei messaggi .
Esistono soluzioni molto popolari oltre a questo, a seconda del caso d'uso.

Un esempio reale di un carico di lavoro asincrono:
a) Commit asincroni in Postgres ↗ .
b) I/O asincrono in Linux (io-uring).
c) I/O asincrono fsync (fs-cache): ogni volta che scriviamo qualcosa su qualsiasi file, non viene scritto direttamente sul disco ma nella cache del file system. C'è una cache nel sistema operativo e le scritture vanno nelle pagine. E poi, il sistema operativo svuota tutte le pagine in una volta sola.

Possiamo riassumere l'intera cosa come segue:
a) L'approccio di programmazione sincrona esegue le attività in sequenza. Ogni attività viene eseguita dopo aver atteso il completamento di qualsiasi attività precedente.
b) Quando un'attività viene eseguita in un modello di programmazione asincrona, possiamo passare a un'altra senza attendere il completamento della precedente.

Grazie per aver letto; Spero sia stato utile.
In caso di ulteriori dubbi, sentiti libero di connetterti su LinkedIn / Instagram .