Cel mai bun răspuns
Răspunsul la întrebarea așa cum este pus este: este la fel ca diferența dintre „unul „și„ mulți ”.
Dar cred că ceea ce probabil ați vrut să spuneți este ceva mai asemănător: cum diferă metodologiile de programare pentru programarea într-un singur fir într-un singur proces comparativ cu în mai multe fire într-un singur proces?
Au fost scrise cărți despre subiect. Dar, pe scurt, diferențele se referă la un singur factor: atunci când programezi într-un singur fir și proces, știi întotdeauna cum ai ajuns la instrucțiunea curentă. Instrucțiunile apar într-o singură secvență. Dar când programați pentru mai multe fire, nu aveți idee ce instrucțiuni au fost executate în ce ordine pentru a ajunge la instrucțiunea curentă. Acest lucru face programarea mai complicată.
Dar mai există un alt factor care o complică: spre deosebire de procese, firele partajează memoria. Nu știți ce fir a atins ultima dată o anumită locație de memorie sau care va fi următorul, cu excepția cazului în care aveți un fel de „sincronizare”. Prin urmare, cuvântul cheie „sincronizat” în Java (extras din Ada). Monitoare, semafore, blocări , variabilele de condiție și chiar transmiterea mesajelor au fost toate utilizate pentru sincronizare.
Răspuns
(Actualizare: Ferește-te de un alt răspuns care spune că nu poți avea un server web cu un singur fir care gestionează bine solicitările concurente, deoarece acest lucru pur și simplu nu este adevărat.)
De ce este un server web multithread mai bun decât un singur server thread? Nu este.
Există patru moduri de bază în care un server web poate gestiona concurența:
- forking un proces de operare pe cerere (cum ar fi versiunile vechi ale Apache)
- generând un fir de operare pe cerere (cum ar fi o nouă versiune a Apache)
- utilizând o buclă de eveniment cu un singur fir (cum ar fi nginx )
- folosind fire verzi sau procese ușoare programat de un Runtime VM în loc de sistemul de operare (ca în Erlang)
În prezent cele mai comune abordări sunt numărul 2 și 3.
Există argumente pro și contra ambelor dintre ei. Pentru operațiile I / O-bound (o caracteristică a unui server web tipic) obțineți performanțe mai bune și număr mai mare de solicitări concurente atunci când utilizați o buclă de eveniment cu un singur fir . Dar dezavantajul este că trebuie să utilizați exclusiv I / O asincrone non-blocante pentru toate operațiunile sau altfel veți bloca bucla evenimentului și veți pierde performanța. Din acest motiv, este mai ușor să implementați un server cu mai multe fire, dar plătiți pentru performanță.
Pentru operațiile legate de CPU (mai puțin frecvente pentru un server web obișnuit, poate mai obișnuit pentru un API intens din punct de vedere al calculului) este mai bine să aveți un fir OS sau proces pe nucleu . Este ușor de realizat cu bucle de evenimente cu un singur fir, deoarece puteți rula un cluster de mai multe procese unul pe nucleu. Este greu de făcut cu servere cu mai multe fire, deoarece dacă firele de reproducere sunt singurul mod de a gestiona solicitările simultane, atunci nu puteți controla cu adevărat câte fire de lucru veți avea – deoarece nu controlați numărul de solicitări. Odată ce aveți mai multe fire decât numărul de nuclee CPU, pierdeți performanța pentru comutatoare de context și utilizați, de asemenea, o mulțime de memorie RAM.
De aceea, un server nginx cu un singur fir funcționează mai bine decât un server web Apache cu mai multe fire (de aceea nginx a fost creat în primul rând). De asemenea, Redis , o bază de date cunoscută pentru performanțe extrem de ridicate este single-threaded .
Un exemplu real pe care vi-l pot oferi este următorul: Primul meu server web a fost Apache care rulează pe o mașină Linux cu 500 MB de RAM. A furnizat un nou proces pentru fiecare cerere (avea de fapt un fond, deci nu era nevoie de multă bifurcare, dar trebuia să mențină acele procese în viață pentru a le reutiliza și a le ucide din când în când pentru a evita scurgerea resurselor). p> Sistemul meu de operare a folosit aproximativ 100 MB de memorie RAM. Fiecare proces Apache a folosit 20 MB de RAM. Însemna că serverul meu putea gestiona doar 20 de cereri concurente și nu exista nicio cale de rezolvare pentru că nu mai aveam RAM. Procesele au fost în mare parte blocate pe I / O, astfel încât utilizarea procesorului a fost foarte scăzută, fiecare cerere peste cele 20 a trebuit să aștepte și dacă aceste 20 au fost de ex. Descărcări de lungă durată, atunci serverul meu nu răspunde complet.
Când a fost introdus serverul web nginx, acesta a folosit o buclă de eveniment cu un singur fir și nu s-a blocat pentru nicio cerere. S-ar putea ocupa de cereri mult mai concurente, neavând nicio problemă cu mitica problemă c10k – nginx a fost creat în principiu pentru a rezolva problema c10k (10.000 de cereri concurente). ar putea genera chiar atât de mult și cât timp este folosit pentru comutatoarele de context.
Utilizarea memoriei pentru Apache multi-thread vs nginx single-thread:
De altfel, acesta este motivul pentru care Ryan Dahl a folosit o I / O non-blocantă și o buclă de eveniment cu un singur fir în Node.js și el folosește în continuare aceeași idee în Deno, deoarece aceasta este modalitatea de a scrie servere de rețea de înaltă performanță (contrar a ceea ce ați putea citi în alte răspunsuri aici).