Subiect de discuții chestii php. Selectarea versiunii PHP pentru Windows. Calea Jedi - Folosind extensia PCNTL

02.04.2020 Știri

Mama unui bun prieten de-al meu și-a pierdut valiza într-un zbor de la Sankt Petersburg la Moscova și a mai avut un zbor către țărmuri mai calde, iar acum era deja în stațiune - fără costum de baie, sandale și doar cu T. -cămașă din bagajul ei de mână. De dragul vremurilor de demult, i-am dat câteva sfaturi despre ce să facă și unde să alerge, iar astăzi am decis să scriu aici tot ce știu despre un anumit subiect.

Pentru a explica unde sunt atât de inteligent, permiteți-mi să vă reamintesc că, la un moment dat, am lucrat în handling la sol pentru mai multe companii aeriene, inclusiv cu anumite probleme legate de căutarea bagajelor. Ei bine, plus propria mea experiență de zbor, desigur. Cu toate acestea, pentru că Am părăsit industria serviciilor de aviație în urmă cu câțiva ani, poate că unele nuanțe s-ar fi schimbat - dacă da, voi accepta cu recunoștință comentariile pe această temă și voi corecta informațiile din postare.

Voi începe cu asta Ce trebuie să faceți pentru a preveni pierderea bagajelor:
1. Scoateți toate etichetele și autocolantele din călătoriile anterioare din valiză, chiar și cele mici cu un cod de bare, care sunt adesea lipite separat de valiza în sine - pot fi confuze sistem automat scanarea si sortarea bagajelor.
2. Agățați o etichetă cu numele pe valiză (geantă, cutie, pachet - în general, tot ceea ce înregistrați ca bagaj): puteți cumpăra o opțiune reutilizabilă în avans sau puteți lua o etichetă de hârtie la ghișeul de check-in - de obicei toate companiile aeriene mai mult sau mai puțin decente le oferă fără restricții. Și Emirates, de exemplu, are în general etichete excelente de plastic pe un cablu durabil, care poate dura foarte mult timp:

Vechii paranoici pot face ca mine: am întotdeauna o etichetă de plastic reutilizabilă din setul Samsonite atârnată pe valiză cu adresa mea permanentă de acasă, numărul de telefon și prin e-mail, iar când zbor undeva în vacanță, închid suplimentar o notă de hârtie pe care indic datele șederii mele în noul loc și toate contactele posibile (numele și adresa hotelului, numărul de telefon local, dacă este disponibil, și numele și numele meu, desigur).
3. La ghișeul de check-in, asigurați-vă că eticheta de bagaj imprimată de agentul de check-in este aplicată pe bagaj - cu codul orașului în care zburați și numărul zborului.
4. Dacă aveți mai multe zboruri de legătură, informați agentul de check-in despre acest lucru și specificați până la ce punct doriți să vă verificați bagajele. În unele cazuri, bagajele vor trebui ridicate de la unul sau altul aeroport de-a lungul rutei, indiferent de dorința dvs.: acest lucru se aplică, de exemplu, transferurilor între aeroporturi (Orly și Charles de Gaulle la Paris, Domodedovo - Sheremetyevo - „Vnukovo „ în Moscova), terminale separate (terminalele 1 și 2 din Frankfurt) sau la primul punct de sosire în SUA sau Mexic - aceasta este o cerință vamală în aceste țări: să presupunem că zburați Moscova-Washington-Phoenix, o etichetă de bagaj este emis pentru toate cele trei segmente către Phoenix, dar în Washington, bagajele vor trebui să fie ridicate fizic, vămuite și înregistrate din nou. De asemenea, dacă vă înregistrați un cărucior pentru copii pe care vi s-a permis să îl luați înainte de a vă urca în avion sau un animal, probabil că va trebui să-l ridicați dintr-un punct de tranzit. În general, în cazul unei rute complexe cu transferuri, este mai bine să clarificați în prealabil detaliile mișcărilor bagajelor la call center-ul companiei aeriene sau, în cazuri extreme, la check-in.
5. Faceți-vă bagajele vizibile: întârzierile bagajelor nu sunt întotdeauna din vina manipulatorilor de bagaje sau defecțiunile sistemului de sortare. Uneori, un alt pasager absent, obosit după un zbor lung, va lua din caruselul de bagaje aceeași geantă de sport Samsonite neagră sau nedescris ca a ta. Prin urmare, marcați-vă bagajele: agățați o grămadă de panglici strălucitoare sau o jucărie moale mică de mâner, lipiți pe el un autocolant mare sau pur și simplu acordați preferință unei culori neobișnuite atunci când alegeți o valiză.

Ce nu ar trebui să fie înregistrat în bagaj?
Nu uitați, toate companiile aeriene și aeroporturile pierd bagajele. Desigur, statisticile sunt diferite pentru fiecare, dar chiar și cele mai de încredere companii aeriene pot pierde sau întârzia bagajele, și chiar și la cel mai mic aeroport, unde un singur manipulator de bagaje va transporta un cărucior cu valize direct de la ghișeul de check-in la avion. Prin urmare, vă sfătuiesc să luați întotdeauna în bagajul de mână:
- documente importante, inclusiv cele care nu sunt necesare în timpul zborului (de exemplu, la ultima mea călătorie la Sankt Petersburg am avut nevoie să-mi schimb permisul și am luat cu mine în bagajul de mână un certificat de căsătorie și tot felul de carduri de la o scoala de soferi)
- chei (în combinație cu o etichetă cu adresa dvs., acest lucru poate fi periculos)
- bani, bijuterii (fără comentarii)
- echipamente fragile scumpe
- medicamente pe care le iei în mod regulat, în cantitatea necesară zborului, și cu o mică rezervă în cazul în care trebuie să cauți un analog într-o țară sau oraș străin. Medicamente pe bază de prescripție medicală care nu pot fi achiziționate în cazul pierderii bagajului, luați cu dvs. în cantitatea necesară pentru întreaga călătorie.
- ceva care poate fi necesar urgent la sosire (de exemplu, Încărcător pentru telefon
- ceva care are valoare sentimentală pentru tine personal: uneori bagajele se pierd pentru totdeauna, iar dacă pierderea jurnalului personal îți rupe inima, este mai bine să-l lași acasă sau să-l iei cu tine în avion

O poveste instructivă: în timpul muncii mele la Lufthansa din Sankt Petersburg, un cuplu căsătorit din SUA a venit în fugă la biroul nostru, strângându-și mâinile - bagajul lor, care conținea documente foarte importante pentru procesul de adopție, nu sosise, procesul a fost a doua zi. Desigur, compania aeriană este de vină pentru pierderea bagajelor, dar cine beneficiază de asta? Pentru a evita o astfel de situație, a fost suficient să pui pur și simplu hârtii importante în bagajul de mână.

Deci, ai ajuns și nu ți-ai găsit bagajele pe centura de bagaje. Ce să fac?
1. Dacă ați înregistrat în bagaj altceva decât valizele obișnuite: schiuri, un violoncel, un panou cu plasmă de dimensiunea peretelui, un cărucior pentru copii, un Great Dane din marmură vie, verificați dacă există un punct separat pentru eliberarea așa-numitului. Bagaj supradimensionat sau Bagaj voluminos - bagajele asemănătoare cu cel descris mai sus sunt adesea încărcate într-un compartiment separat și descărcate separat, manual. Dacă nici bagajul tău nu se găsește acolo
2. Accesați ghișeul de urmărire a bagajelor sau Lost & Found. Acolo va trebui să completați un formular special cu informatii detaliate despre bagajele dvs.: traseu, aspect, o scurtă listă de conținut, contactele dvs. la locul de reședință permanentă și șederea temporară. De asemenea, în serviciul de urmărire a bagajelor este mai probabil să vedeți un grafic al bagajelor ca acesta:

În conformitate cu această clasificare, bagajele tale lipsă vor fi codificate și, ca să înțelegi, aceste două valize vor fi codificate la fel:

Așa că nu ezitați să adăugați detalii suplimentare în descriere și nu sări peste clauza de conținut. De regulă, atunci când completați pentru prima dată un raport de întârziere a bagajelor, vi se va cere să indicați mai multe elemente de conținut prin care bagajul dvs. poate fi identificat dacă nu există semne de identificare pe exterior și va trebui deschisă geanta (dacă sacul este deschis, vei pune notificare despre asta). Exemplu prost: tricou / carte / șervețele umede, exemplu bun: bikini roșu aprins / catalog reproduceri Malevich / fier pliabil. După completarea cererii, angajatul serviciului de urmărire a bagajelor vă va oferi un număr în formatul XXXYY11111, unde XXX este codul aeroportului de sosire, YY este codul companiei aeriene de sosire + 5 cifre ale numărului de serie al aplicației: de exemplu, JFKLH12345, dacă ai zburat cu Lufthansa la aeroportul Kennedy din New York. Amintiți-vă sau notați acest număr - va fi cel mai simplu mod de a vă găsi aplicația în aplicațiile viitoare.
Folosind același număr, puteți verifica starea căutării TU ȘI (din anumite motive link-ul dispare: dacă nu funcționează pentru tine, google World Tracer Online și literalmente al doilea link - cu titlul Baggage Tracing de pe site-ul worldtracer.aero - este ceea ce ai nevoie), pentru că să ajungi la lost&found este adesea foarte dificil
3. Încercați să contactați biroul companiei dumneavoastră aeriene de la aeroportul de sosire: uneori (subliniez – CATEATEA!) dacă nu ați zburat acasă, ci într-un loc de ședere temporară (vacanță, călătorie de afaceri), compania aeriană vă poate oferi un set de articole de toaletă (Lufthansa are) inclus un tricou supradimensionat, o periuță de dinți și pastă, un pieptene, pachete mici de șampon și gel de duș, un pachet de pudră de spălat etc.) sau efectuați o mică plată în numerar pentru cheltuielile mici pe la fața locului (plată în numerar).

Ce se va întâmpla în continuare?
Dosarul dumneavoastră (așa-numitul AHL) va merge în sistemul centralizat de căutare a bagajelor (World Tracer). Toate bagajele nerevendicate se încadrează în același sistem de căutare, indiferent dacă au fost găsite fără etichetă în colțurile curții de bagaje sau au rămas pe centura de bagaje; pentru fiecare dintre aceste articole, un fișier în formatul XXXYY11111 este de asemenea creat, doar de alt subtip - așa-numitul. raport la îndemână sau OHD. În cazul în care datele din fișierele AHL și OHD coincid (numele, descrierea valizei, traseul etc.), ambele stații (unde s-a semnalat pierderea bagajului și unde a fost găsit bagajul nerevendicat) vor primi o notificare, iar apoi este o chestiune de tehnologie: reverificarea si in caz de succes trimiterea bagajelor in orasul dorit. Desigur, este și o mulțime de muncă manuală implicată - schimbul de mesaje, respingerea valizelor similare, dar nu aceleași, plus răspunsul la mai multe apeluri telefonice- in general, personalul serviciului de cautare bagaje nu se plictiseste niciodata.
Statistici aproximative: peste 90% din bagajele pierdute sunt găsite în primele 3 zile de căutare, 3% sunt pierdute pentru totdeauna.
Ce poti face?
1. Dacă trebuie să cumpărați ceva necesar urgent la sosire (de la o periuță de dinți la un costum de afaceri), asigurați-vă că păstrați chitanțele pentru compensare ulterioară. Cu toate acestea, ar trebui să evitați achizițiile costisitoare inutile; voi explica de ce mai târziu.
2. Urmând pași noi, alcătuiește cea mai detaliată listă de conținut, de preferință cu culoarea, marca și costul aproximativ totul, ideal Limba engleză(pentru că altfel un angajat al unei companii aeriene va trebui să traducă această listă pentru includerea în sistem), contactați compania aeriană și trimiteți-le această listă, aceasta va fi adăugată în aplicația de căutare bagaje. În primele 5 zile, căutarea bagajelor este efectuată de către aeroportul de sosire, apoi căutarea devine responsabilitatea companiei aeriene transportatoare (compania aeriană care este indicată în numărul cererii - vă amintiți JFKLH12345?), iar după 21 de zile poate solicita despăgubiri finale.
3. Dacă, după 21 de zile de la data depunerii declarației privind bagajele pierdute, acesta nu a fost găsit, contactați compania aeriană transportatoare pentru a solicita despăgubiri. Dacă nu mă înșel, termenul de prescripție este de 2 ani, adică. Puteți solicita despăgubiri în termen de doi ani de la data depunerii cererii pentru pierdere.

Plata despăgubirii.
Pentru a plăti despăgubiri, va trebui să contactați reprezentanța companiei dumneavoastră aeriene cu o cerere de plată, documente care confirmă zborul și faptul pierderii bagajelor (carte de îmbarcare, etichete pentru bagaje, număr de cerere pentru pierderea bagajului, detalii de plată). Dacă nu mă înșel, în Federația Rusă o decizie cu privire la despăgubire trebuie luată în considerare din punct de vedere legal în termen de 30 de zile. De asemenea, vi se poate cere să estimați costul conținutului și, dacă este posibil, să furnizați chitanțe pentru achiziționarea valizei și a articolelor din ea (înțeleg că acest lucru este nerealist în majoritatea cazurilor, dar aceasta face parte din procedură).
Anterior, plățile se făceau în funcție de greutatea bagajului de cală - aproximativ 20 de dolari pe kilogram. Ulterior, sistemul de plată a fost schimbat și răspunderea companiilor aeriene a fost limitată la 1.000 de unități convenționale (costul unei unități convenționale se calculează în cadrul companiei aeriene), care la momentul muncii mele corespundea la aproximativ 1.300 de euro. Acestea. chiar dacă ai o chitanță pentru achiziționarea unei valize Louis Vuitton din o mie de piei de gecko bolivieni și umplute cu diamante, nu vei primi mai mult de 1.300 de euro.

Uneori devine necesar să se efectueze mai multe acțiuni simultan, de exemplu, verificarea modificărilor dintr-un tabel al bazei de date și efectuarea modificărilor la altul. Mai mult, dacă una dintre operații (de exemplu, verificarea modificărilor) durează mult, este evident că execuția secvențială nu va asigura echilibrarea resurselor.

Pentru a rezolva acest tip de problemă, programarea folosește multithreading - fiecare operație este plasată într-un fir separat cu o cantitate alocată de resurse și funcționează în interiorul acestuia. Cu această abordare, toate sarcinile vor fi îndeplinite separat și independent.

Deși PHP nu acceptă multithreading, există mai multe metode de emulare, care vor fi discutate mai jos.

1. Rularea mai multor copii ale scriptului - o copie per operație

//woman.php if (!isset($_GET["thread"])) ( system("wget ​​​​http://localhost/woman.php?thread=make_me_happy"); system("wget ​​​​http: //localhost/ woman.php?thread=make_me_rich"); ) elseif ($_GET["thread"] == "make_me_happy") ( make_her_happy(); ) elseif ($_GET["thread"] == "make_me_rich" ) (găsește_altul_unul(); )

Când executăm acest script fără parametri, rulează automat două copii ale lui, cu ID-uri de operație ("thread=make_me_happy" și "thread=make_me_rich"), care inițiază execuția funcțiilor necesare.

Astfel obținem rezultatul dorit - două operații sunt efectuate simultan - dar acesta, desigur, nu este multithreading, ci pur și simplu o cârjă pentru îndeplinirea sarcinilor simultan.

2. Calea Jedi - folosind extensia PCNTL

PCNTL este o extensie care vă permite să lucrați pe deplin cu procesele. Pe lângă management, acceptă trimiterea de mesaje, verificarea stării și stabilirea priorităților. Iată cum arată scriptul anterior folosind PCNTL:

$pid = pcntl_fork(); if ($pid == 0) ( make_her_happy(); ) elseif ($pid > 0) ( $pid2 = pcntl_fork(); if ($pid2 == 0) ( find_nother_one(); ) )

Pare destul de confuz, hai să trecem prin el rând cu rând.

În prima linie, „furcăm” procesul curent (furcația este copierea unui proces, păstrând în același timp valorile tuturor variabilelor), împărțindu-l în două procese (curent și copil) care rulează în paralel.

Pentru a înțelege unde suntem acest moment, într-un proces copil sau mamă, funcția pcntl_fork returnează 0 pentru copil și ID-ul procesului pentru mamă. Prin urmare, în a doua linie, ne uităm la $pid, dacă este zero, atunci suntem în procesul copil - executăm funcția, în caz contrar, suntem în mamă (linia 4), apoi creăm un alt proces și efectuează în mod similar sarcina.

Procesul de executare a scriptului:

Astfel, scriptul creează încă 2 procese copil, care sunt copiile sale și conțin aceleași variabile cu valori similare. Și folosind identificatorul returnat de funcția pcntl_fork, aflăm în ce fir ne aflăm în prezent și efectuăm acțiunile necesare.

Se pare că dezvoltatorii PHP folosesc rar concurența. Nu voi vorbi despre simplitatea codului sincron; programarea cu un singur thread, desigur, este mai simplă și mai clară, dar uneori utilizare mică paralelismul poate aduce îmbunătățiri semnificative de performanță.

În acest articol, vom arunca o privire asupra modului în care multithreading poate fi realizat în PHP folosind extensia pthreads. Pentru a face acest lucru, veți avea nevoie de versiunea ZTS (Zend Thread Safety) a PHP 7.x instalată, împreună cu extensia instalată pthreads v3. (La momentul scrierii, în PHP 7.1, utilizatorii vor trebui să instaleze din ramura principală din depozitul pthreads - vezi extensia terță parte.)

O mică precizare: pthreads v2 este destinat PHP 5.x și nu mai este acceptat, pthreads v3 este pentru PHP 7.x și este în curs de dezvoltare.

După o astfel de digresiune, să trecem direct la subiect!

Prelucrarea sarcinilor unice

Uneori doriți să procesați sarcini unice într-un mod cu mai multe fire (de exemplu, executând unele sarcini legate de I/O). În astfel de cazuri, puteți utiliza clasa Thread pentru a crea un fir nou și pentru a rula unele procesări pe un fir separat.

De exemplu:

$task = noua clasă extinde Thread ( private $response; public function run() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+)~", $conținut, $potriviri); $this->response = $match; ) ); $task->start() && $task->join(); var_dump($task->response); // șir (6) „Google”

Aici metoda de rulare este procesarea noastră, care va fi executată într-un fir nou. Când Thread::start este apelat, este generat un nou thread și este apelată metoda de rulare. Apoi unim firul copil înapoi la firul principal apelând Thread::join , care se va bloca până când firul copil se va termina de execuție. Acest lucru asigură că sarcina se termină de executat înainte de a încerca să tipărim rezultatul (care este stocat în $task->response).

Este posibil să nu fie de dorit să poluăm o clasă cu responsabilități suplimentare asociate cu logica fluxului (inclusiv responsabilitatea definirii unei metode de rulare). Putem distinge astfel de clase prin moștenirea lor din clasa Threaded. Apoi pot fi rulate într-un alt thread:

Class Task extinde Threaded ( public $response; funcția publică someWork() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+) ~", $content, $match); $ this->response = $potrivește; ) ) $sarcină = sarcină nouă; $thread = clasă nouă($sarcină) extinde Thread (privată $sarcină; funcția publică __construct(Thread $sarcină) ( $aceasta->sarcină = $sarcină; ) funcția publică run() ( $aceasta->sarcină->someWork( ); )); $thread->start() && $thread->join(); var_dump($sarcină->răspuns);

Orice clasă care trebuie rulată într-un fir separat trebuie sa moștenește din clasa Threaded. Acest lucru se datorează faptului că oferă capabilitățile necesare pentru a efectua procesări pe diferite fire, precum și securitate implicită și interfețe utile (cum ar fi sincronizarea resurselor).

Să aruncăm o privire la ierarhia claselor oferită de extensia pthreads:

Filet (implementează Traversable, Collectable) Thread Worker Pool Volatile

Am acoperit și am învățat deja elementele de bază ale claselor Thread și Threaded, acum să aruncăm o privire la celelalte trei (Worker, Volatile și Pool).

Reutilizarea firelor

Începerea unui fir nou pentru fiecare sarcină care trebuie paralelizată este destul de costisitoare. Acest lucru se datorează faptului că o arhitectură comun-nimic trebuie implementată în pthreads pentru a realiza multithreading în PHP. Ceea ce înseamnă că întregul context de execuție al instanței curente a interpretului PHP (inclusiv fiecare clasă, interfață, trăsătură și funcție) trebuie copiat pentru fiecare fir creat. Deoarece acest lucru are un impact vizibil asupra performanței, fluxul ar trebui să fie întotdeauna reutilizat ori de câte ori este posibil. Threadurile pot fi reutilizate în două moduri: folosind Workers sau folosind Pools.

Clasa Worker este folosită pentru a efectua o serie de sarcini sincron în cadrul unui alt fir. Acest lucru se face prin crearea unei noi instanțe Worker (care creează un fir nou), apoi împingând sarcini în stiva acelui fir separat (folosind Worker::stack).

Iată un mic exemplu:

Class Task se extinde Threaded ( private $valoare; funcția publică __construct(int $i) ( $this->value = $i; ) public function run() ( usleep(250000); echo "Task: ($this->value) \n"; ) ) $lucrător = nou Lucrător(); $lucrător->start(); for ($i = 0; $i stack(new Task($i)); ) while ($worker->collect()); $worker->shutdown();

În exemplul de mai sus, 15 sarcini pentru un nou obiect $worker sunt împinse în stivă prin metoda Worker::stack și apoi sunt procesate în ordinea în care au fost împinse. Metoda Worker::collect, așa cum se arată mai sus, este utilizată pentru a curăța sarcinile de îndată ce se termină de executat. Cu ea, într-o buclă while, blocăm firul principal până când toate sarcinile de pe stivă sunt finalizate și șterse - înainte de a apela Worker::shutdown . Terminarea timpurie a unui lucrător (adică în timp ce încă mai sunt sarcini care trebuie finalizate) va bloca în continuare firul principal până când toate sarcinile își vor finaliza execuția, doar că sarcinile nu vor fi colectate de gunoi (ceea ce implică pierderi de memorie).

Clasa Worker oferă câteva alte metode legate de stiva sa de sarcini, inclusiv Worker::unstack pentru eliminarea ultimei sarcini stivuite și Worker::getStacked pentru obținerea numărului de sarcini din stiva de execuție. Stiva unui lucrător conține doar sarcinile care trebuie executate. Odată ce o sarcină din stivă a fost finalizată, aceasta este eliminată și plasată pe o stivă separată (internă) pentru colectarea gunoiului (folosind metoda Worker::collect).

O altă modalitate de a reutiliza un fir de execuție în mai multe sarcini este utilizarea unui pool de fire de execuție (prin clasa Pool). Un grup de fire de execuție folosește un grup de lucrători pentru a permite executarea sarcinilor simultan, în care factorul de concurență (numărul de fire de execuție cu care operează) este setat la crearea grupului.

Să adaptăm exemplul de mai sus pentru a folosi un grup de lucrători:

Class Task se extinde Threaded ( private $valoare; funcția publică __construct(int $i) ( $this->value = $i; ) public function run() ( usleep(250000); echo "Task: ($this->value) \n"; ) ) $pool = pool nou(4); for ($i = 0; $i submit(new Task($i)); ) while ($pool->collect()); $pool->shutdown();

Există câteva diferențe notabile atunci când utilizați o piscină, spre deosebire de un lucrător. În primul rând, pool-ul nu trebuie să fie pornit manual; acesta începe să execute sarcini de îndată ce acestea devin disponibile. În al doilea rând, noi trimite sarcini la piscină, nu pune-le pe un teanc. În plus, clasa Pool nu moștenește din Threaded și, prin urmare, nu poate fi transmisă altor fire (spre deosebire de Worker).

Este o bună practică pentru lucrători și piscine să își curețe întotdeauna sarcinile de îndată ce le-au finalizat și apoi să le încheie manual ei înșiși. Firele create folosind clasa Thread trebuie, de asemenea, atașate firului părinte.

pthreads și (im)mutabilitatea

Ultima clasă pe care o vom atinge este Volatile, o nouă adăugare la pthreads v3. Imuabilitatea a devenit un concept important în pthreads, deoarece fără ea, performanța suferă semnificativ. Prin urmare, în mod implicit, proprietățile claselor Threaded care sunt ele însele obiecte Threaded sunt acum imuabile și, prin urmare, nu pot fi suprascrise după atribuirea lor inițială. Mutabilitatea explicită pentru astfel de proprietăți este în prezent preferată și poate fi încă obținută folosind noua clasă Volatile.

Să ne uităm la un exemplu care va demonstra noile restricții de imuabilitate:

Class Task extinde Threaded // o clasă Threaded (funcția publică __construct() ( $this->data = new Threaded(); // $this->data nu poate fi suprascris, deoarece este o proprietate Threaded a unei clase Threaded) ) $task = new class(new Task()) extinde Thread ( // o clasă Threaded, deoarece Thread extinde Threaded funcția publică __construct($tm) ( $this->threadedMember = $tm; var_dump($this->threadedMember-> date); // obiect(Threaded)#3 (0) () $this->threadedMember = new StdClass(); // invalid, deoarece proprietatea este un membru Threaded al unei clase Threaded ));

Proprietățile filetate ale claselor volatile, pe de altă parte, sunt mutabile:

Class Task extinde Volatile (funcția publică __construct() ( $this->data = new Threaded(); $this->data = new StdClass(); // valid, deoarece suntem într-o clasă volatilă ) ) $task = new class(new Task()) extinde Thread (funcția publică __construct($vm) ( $this->volatileMember = $vm; var_dump($this->volatileMember->data); // obiect(stdClass)#4 (0) () // încă invalid, deoarece Volatile extinde Threaded, deci proprietatea este încă un membru Threaded al clasei Threaded $this->volatileMember = new StdClass(); ) );

Putem vedea că clasa Volatile suprascrie imuabilitatea impusă de clasa părinte Threaded pentru a oferi posibilitatea de a schimba proprietățile Threaded (precum și unset()).

Există un alt subiect de discuție pentru a acoperi tema variabilității și a clasei Volatile - matrice. În pthreads, matricele sunt turnate automat în obiecte Volatile atunci când sunt atribuite unei proprietăți a clasei Threaded. Acest lucru se datorează faptului că pur și simplu nu este sigur să manipulați o serie de mai multe contexte PHP.

Să ne uităm din nou la un exemplu pentru a înțelege mai bine unele lucruri:

$matrice = ; $sarcină = clasă nouă ($array) extinde Thread ( private $date; public function __construct(array $array) ( $this->data = $array; ) public function run() ( $this->data = 4; $ this->data = 5; print_r($this->data); ) ); $sarcină->start() && $sarcină->join(); /* Ieșire: obiect volatil ( => 1 => 2 => 3 => 4 => 5) */

Vedem că obiectele volatile pot fi tratate ca și cum ar fi matrice, deoarece acceptă operații cu matrice, cum ar fi (așa cum se arată mai sus) operatorul subset(). Cu toate acestea, clasele Volatile nu acceptă funcții de bază ale matricei, cum ar fi array_pop și array_shift. În schimb, clasa Threaded ne oferă astfel de operații ca metode încorporate.

Ca demonstrație:

$date = clasă nouă se extinde Volatil ( public $a = 1; public $b = 2; public $c = 3; ); var_dump($date); var_dump($date->pop()); var_dump($date->shift()); var_dump($date); /* Ieșire: obiect(clasă@anonim)#1 (3) ( ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) ) int(3) int(1) obiect(clasa@anonim)#1 (1) ( ["b"] => int(2) ) */

Alte operațiuni acceptate includ Threaded::chunk și Threaded::merge .

Sincronizare

În ultima secțiune a acestui articol, ne vom uita la sincronizarea în pthreads. Sincronizarea este o metodă care vă permite să controlați accesul la resursele partajate.

De exemplu, să implementăm un contor simplu:

$counter = noua clasa extinde Thread ( public $i = 0; public function run() ( for ($i = 0; $i i; ) ) ); $contor->start(); pentru ($i = 0; $i i; ) $counter->join(); var_dump($contor->i); // va tipări un număr de la 10 la 20

Fără utilizarea sincronizării, ieșirea nu este deterministă. Mai multe fire de execuție scriu pe aceeași variabilă fără acces controlat, ceea ce înseamnă că actualizările se vor pierde.

Să reparăm acest lucru, astfel încât să obținem rezultatul corect de 20 prin adăugarea de sincronizare:

$counter = noua clasa extinde Thread ( public $i = 0; public function run() ( $this->synchronized(function () ( for ($i = 0; $i i; ) )); ) ); $contor->start(); $contor->sincronizat(funcție ($contor) ( pentru ($i = 0; $i i; ) ), $contor); $counter->join(); var_dump($contor->i); // int(20)

Blocurile de cod sincronizate pot comunica, de asemenea, între ele folosind metodele Threaded::wait și Threaded::notify (sau Threaded::notifyAll).

Iată un increment alternativ în două bucle while sincronizate:

$counter = noua clasa extinde Thread ( public $cond = 1; public function run() ( $this->synchronized(function ()) (pentru ($i = 0; $i notify();); if ($this->cond === 1) ( $this->cond = 2; $this->wait(); ) ) )); ) ); $contor->start(); $contor->sincronizat(funcție ($contor) ( if ($contor->cond !== 2) ( $contor->wait(); // așteptați ca celălalt să pornească primul ) pentru ($i = 10; $i notify(); if ($counter->cond === 2) ( $counter->cond = 1; $counter->wait(); ) ) ), $contor); $counter->join(); /* Ieșire: int(0) int(10) int(1) int(11) int(2) int(12) int(3) int(13) int(4) int(14) int(5) int( 15) int(6) int(16) int(7) int(17) int(8) int(18) int(9) int(19) */

S-ar putea să observi conditii suplimentare, care au fost plasate în jurul apelului la Threaded::wait . Aceste condiții sunt critice deoarece permit reluarea apelului sincronizat atunci când a primit o notificare și condiția specificată este adevărată. Acest lucru este important deoarece notificările pot veni din alte locuri decât atunci când Threaded::notify este apelat. Astfel, dacă apelurile la metoda Threaded::wait nu au fost incluse în condiții, vom executa apeluri false trezire, ceea ce va duce la un comportament imprevizibil al codului.

Concluzie

Am analizat cele cinci clase ale pachetului pthreads (Threaded, Thread, Worker, Volatile și Pool) și cum este utilizată fiecare clasă. Ne-am uitat și la noul concept de imuabilitate în pthreads, made scurtă recenzie capabilități de sincronizare acceptate. Cu aceste elemente de bază, acum putem începe să ne uităm la modul în care pthread-urile pot fi utilizate în cazurile din lumea reală! Acesta va fi subiectul următoarei noastre postări.

Dacă ești interesat de traducerea următoarei postări, anunță-mă: comentează pe rețelele de socializare. rețele, votează pozitiv și distribuie postarea colegilor și prietenilor.

Am încercat recent pthreads și am fost plăcut surprins - este o extensie care adaugă capacitatea de a lucra cu mai multe fire reale în PHP. Fără emulare, fără magie, fără falsuri - totul este real.



Mă gândesc la o astfel de sarcină. Există o serie de sarcini care trebuie finalizate rapid. PHP are alte instrumente pentru rezolvarea acestei probleme, ele nu sunt menționate aici, articolul este despre pthreads.



Ce sunt pthread-urile

Asta e tot! Ei bine, aproape totul. De fapt, există ceva care poate supăra un cititor curios. Toate acestea nu funcționează pentru PHP standard, compilat cu opțiuni implicite. Pentru a vă bucura de multithreading, trebuie să aveți ZTS (Zend Thread Safety) activat în PHP.

Configurare PHP

Apoi, PHP cu ZTS. Nu vă deranjează marea diferență de timp de execuție față de PHP fără ZTS (37.65 vs 265.05 secunde), nu am încercat să generalizez setarea PHP. În cazul fără ZTS, am XDebug activat, de exemplu.


După cum puteți vedea, atunci când utilizați 2 fire, viteza de execuție a programului este de aproximativ 1,5 ori mai mare decât în ​​cazul codului liniar. Când utilizați 4 fire - de 3 ori.


Puteți observa că, deși procesorul este cu 8 nuclee, timpul de execuție al programului a rămas aproape neschimbat dacă s-au folosit mai mult de 4 fire. Se pare că acest lucru se datorează faptului că procesorul meu are 4 nuclee fizice.Pentru claritate, am descris placa sub forma unei diagrame.


rezumat

În PHP, este posibil să lucrați destul de elegant cu multithreading folosind extensia pthreads. Acest lucru oferă o creștere vizibilă a productivității.

Etichete: Adăugați etichete

Atenţie! Acest articol este iremediabil depășit sau este acum evaluat de autor ca neavând niciun beneficiu informațional.

Frumusețea codului open-source este deschiderea lui :)) Adică. dacă aveți inteligență/timp/dorință, vă puteți da seama exact cum funcționează programul. Dezavantajul unui astfel de cod este dificultatea de a obține pachetele compilate necesare. De exemplu, PHP poate fi descărcat ca surse pentru sistemele Nix cu compilare/asamblare ulterioară. Totul este deja asamblat pentru Windows, dar există o mulțime de pachete binare gata făcute! Opțiuni cu „ fire safe/non thread safe", VC6/VC9Și versiuni diferite PHP în sine. Articolul a fost creat pentru a clarifica situația. Se bazează pe diferite surse, parțial pe traducerea din engleză. Totul pentru ca data viitoare să nu mai fiu nevoit să-mi dau seama din nou - „ce rost are!?”

Necesar versiunea PHP depinde de versiunea serverului web pe care va fi utilizat. De exemplu, Apache 1.3.x funcționează cu versiunea PHP 3.0.x, Apache 2.x funcționează cu versiunea PHP 4.0 și mai mare. Dar aceasta nu este o astfel de problemă, concentrează-te pe versiuni mai noi stabile și pe ceea ce are hosterul.

Ce fel de postscripte VC6, VC9, VC11? Sursele PHP pentru Windows sunt compilate în Studio vizual. VC9 este obținut atunci când este compilat în VS 2008, VC11 - Visual Studio 2012. În consecință, pentru ca toate acestea să funcționeze pentru dvs., bibliotecile trebuie să fie instalate pe computer Visual C++ redistribuibil pentru Visual Studio anul corespunzător. Câteva clarificări pe această temă.

În plus, dacă serverul dvs. web este un Apache vechi de pe apache.org, atunci trebuie să descărcați versiunea VC6 a PHP, pentru compilarea căreia a fost folosit Visual Studio 6. Dacă PHP va funcționa pentru IIS sau împreună cu un Apache mai nou , atunci poti colecta ceva mai modern ;)

Pentru mine, principalul obstacol în alegere este hosterul. Acum există o versiune stabilă a PHP 5.5.4, dar încă mai are 5.2.17!

Acum partea cea mai interesantă: " fir de siguranta sau fără fir sigur?"
Traducere gratuită a articolului (Dominic Ryan, 27.09.2007)

Nu am văzut niciodată o astfel de engleză stricata:((Voiam să traduc rapid articolul, dar înțeleg dificultăți în ceea ce a scris autorul. Tranzițiile constante între „ce-este-aia” și propozițiile complexe fac în general Moscova să iasă în evidență. Traducerea în Rusă este la fel de complicată de faptul că nu am suficiente cunoștințe și imaginație despre cum să numesc corect ceva în rusă, care de obicei este scris doar în engleză%). De exemplu, nu am văzut niciodată conceptul tehnic de „arhitectură cu mai multe procese” în rusă, dar perla mea este „nesigură pentru curgere” este în general o chestiune de bun simț. În general, vă voi spune ce sa întâmplat.

Diferență între fir de sigurantaȘi fără fir sigur Pachete binare PHP

De când PHP a apărut pentru prima dată pe Windows pe 20 octombrie 2000 cu PHP 3.0.17, pachetele sale binare au fost întotdeauna construite ca sigur pentru fire (TS). Motivul este următorul: Windows folosește o arhitectură cu mai multe fire, iar sistemele Nix acceptă o arhitectură cu mai multe procese. Dacă PHP a fost compilat ca o aplicație CGI cu mai multe procese în loc de una cu mai multe fire, atunci folosirea lui ca modul CGI sub Windows pe un server IIS duce la încetiniri severe și la utilizarea procesorului. Pe de altă parte, puteți conecta PHP la IIS ca modul ISAPI ( este necesară construcția cu mai multe fire- aprox. traducător). Apoi apare o altă problemă: unele extensii PHP populare sunt proiectate cu Unix/Linux în minte, de exemplu. cu o arhitectură multi-proces, ceea ce duce la blocarea PHP conectat la IIS ca modul ISAPI. Acea. Crearea CGI este cel mai stabil mediu pentru PHP pe IIS, cu principalul dezavantaj că este teribil de lent. Trebuie să încărcăm și să descarcăm întregul mediu PHP din memorie de fiecare dată când există o solicitare.

La acea vreme, existau mai multe opțiuni pentru a îmbunătăți performanța PHP pe IIS. Primul este de a folosi opcode cache cu programe precum eAccelerator, care stochează scripturi PHP într-o stare parțial compilată pe disc și/sau în memorie. Această abordare reduce semnificativ timpul de execuție a scriptului. O altă opțiune a fost configurarea IIS pentru a utiliza PHP în mod FastCGI. În acest caz, procesul PHP nu s-a închis după finalizare, ci a primit o nouă sarcină cu următoarea solicitare PHP. În plus, a fost posibilă rularea mai multor procese PHP în același timp, accelerând semnificativ procesarea cererilor, ceea ce a fost un bonus al modului PHP CGI. Cu toate acestea, este posibil să fi existat probleme minore de compatibilitate cu extensiile PHP. Este încă cel mai mult cale rapidă utilizarea PHP, iar programul de instalare „IIS Aid PHP Installer” este configurat pentru a seta această configurație IIS.

Binare colectate în modul thread-unsafe (non thread safe, NTS), vă permit să configurați IIS (și alte servere web pe Windows) pentru a utiliza PHP ca interfață CGI standard cu o creștere puternică a performanței, deoarece în acest caz (într-o astfel de construcție), procesul PHP nu trebuie să aștepte ca firele să se sincronizeze. Când se compară performanța pachetelor binare PHP „thread safe” și „non thread safe” pe IIS ca interfață CGI standard, creșterea performanței este de până la 40%, dar încă nu este la fel de rapidă ca utilizarea unui opcode în metoda FastCGI . Și cea mai mare problemă este că nu puteți utiliza în mod fiabil binarele nesigure pentru fire împreună cu cele sigure pentru fire. Aceasta înseamnă că nu puteți utiliza sisteme de stocare în cache opcode precum eAccelerator într-un mediu PHP creat de pachete binare nesigure pentru fire (o declarație care este corectă la momentul scrierii).

Dacă PHP nesigur pentru fire nu poate fi configurat la aceeași viteză ca un mediu sigur pentru fire, atunci de ce este necesar într-o astfel de versiune? Să revenim la evoluțiile FastCGI și Microsoft în acest domeniu în ultimii câțiva ani. Codificatorii mici și soft au creat propria lor versiune de FastCGI, care vă permite să configurați binare PHP nesigure pentru fire în modul FastCGI, ceea ce aduce performanță la viteza luminii :)

Din articol am ajuns la concluzia că frânele sunt respectate doar atunci când sunt folosite cu serverul web IIS. În orice caz, nu am văzut nimic stupid sub Windows+Apache. De asemenea, spune că puteți overclocka ansamblul NTS orice server web, dar nu îmi pot imagina o astfel de configurație Apache.