Okna.  Wirusy.  Laptopy.  Internet.  Biuro.  Narzędzia.  Kierowcy

Wygląda na to, że programiści PHP rzadko używają współbieżności. Nie będę mówił o prostocie kodu synchronicznego; programowanie jednowątkowe jest oczywiście prostsze i bardziej przejrzyste, ale czasami niewielkie użycie równoległości może przynieść zauważalny wzrost wydajności.

W tym artykule przyjrzymy się, jak można osiągnąć wielowątkowość w PHP przy użyciu rozszerzenia pthreads. Będzie to wymagało zainstalowania ZTS (Zend Thread Safety). Wersja PHP 7.x wraz z zainstalowane rozszerzenie pthreads v3. (W momencie pisania tego tekstu, w PHP 7.1 użytkownicy będą musieli dokonać instalacji z gałęzi master w repozytorium pthreads - zobacz rozszerzenie strony trzeciej.)

Małe wyjaśnienie: pthreads v2 jest przeznaczony dla PHP 5.x i nie jest już obsługiwany, pthreads v3 jest przeznaczony dla PHP 7.x i jest aktywnie rozwijany.

Po takiej dygresji przejdźmy od razu do rzeczy!

Przetwarzanie zadań jednorazowych

Czasami chcesz przetworzyć jednorazowe zadania w sposób wielowątkowy (na przykład wykonując zadanie powiązane z we/wy). W takich przypadkach możesz użyć klasy Thread, aby utworzyć nowy wątek i uruchomić pewne przetwarzanie w osobnym wątku.

Na przykład:

$task = nowa klasa rozszerza wątek ( prywatna $odpowiedź; publiczna funkcja run() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+)~", $treść, $dopasowania); $this->odpowiedź = $dopasowania; ) ); $zadanie->start() && $zadanie->join(); var_dump($zadanie->odpowiedź); // string (6) „Google”

Tutaj metodą run jest nasze przetwarzanie, które zostanie wykonane w nowym wątku. Po wywołaniu Thread::start tworzony jest nowy wątek i wywoływana jest metoda run. Następnie łączymy wątek podrzędny z powrotem do wątku głównego, wywołując Thread::join , który będzie blokowany do czasu zakończenia wykonywania wątku podrzędnego. Dzięki temu zadanie zakończy się przed próbą wydrukowania wyniku (który jest przechowywany w $task->response).

Zanieczyszczanie klasy dodatkowymi obowiązkami związanymi z logiką przepływu (w tym odpowiedzialnością za zdefiniowanie metody uruchamiania) może nie być pożądane. Klasy takie możemy rozróżnić dziedzicząc je z klasy Threaded. Następnie można je uruchomić w innym wątku:

Zadanie klasowe rozszerza Threaded ( public $response; publiczna funkcja SomeWork() ( $content = file_get_contents("http://google.com"); preg_match("~ (.+) ~", $content, $matches); $ this->response = $pasuje;) ) $zadanie = nowe zadanie; $wątek = nowa klasa($zadanie) rozszerza wątek ( prywatne $zadanie; funkcja publiczna __construct(Wątkowe $zadanie) ( $this->task = $zadanie; ) publiczna funkcja run() ( $this->task->someWork( ); $thread->start() && $thread->join(); var_dump($zadanie->odpowiedź);

Dowolna klasa, która musi zostać uruchomiona w osobnym wątku musieć dziedziczyć z klasy Threaded. Dzieje się tak, ponieważ zapewnia niezbędne możliwości przetwarzania w różnych wątkach, a także ukryte zabezpieczenia i przydatne interfejsy (takie jak synchronizacja zasobów).

Przyjrzyjmy się hierarchii klas oferowanej przez rozszerzenie pthreads:

Wątkowa (implementuje przejezdną, kolekcjonerską) pulę nielotną procesu roboczego wątku

Omówiliśmy już i poznaliśmy podstawy klas Thread i Threaded, teraz przyjrzyjmy się pozostałym trzem klasom (Worker, Volatile i Pool).

Ponowne używanie wątków

Rozpoczęcie nowego wątku dla każdego zadania, które należy zrównoleglić, jest dość kosztowne. Dzieje się tak dlatego, że w pwątkach należy zaimplementować architekturę niczego wspólnego, aby osiągnąć wielowątkowość w PHP. Oznacza to, że dla każdego utworzonego wątku należy skopiować cały kontekst wykonania bieżącej instancji interpretera PHP (w tym każdą klasę, interfejs, cechę i funkcję). Ponieważ ma to zauważalny wpływ na wydajność, strumień powinien być zawsze używany ponownie, gdy tylko jest to możliwe. Wątki można ponownie wykorzystać na dwa sposoby: używając pracowników lub korzystając z pul.

Klasa Worker służy do synchronicznego wykonywania wielu zadań w innym wątku. Odbywa się to poprzez utworzenie nowej instancji Worker (która tworzy nowy wątek), a następnie wypychanie zadań na stos tego osobnego wątku (za pomocą Worker::stack).

Oto mały przykład:

Klasa Zadanie rozszerza Wątkowe ( private $value; funkcja publiczna __construct(int $i) ( $this->value = $i; ) funkcja publiczna run() ( usleep(250000); echo "Zadanie: ($this->value) \n"; ) ) $pracownik = nowy pracownik(); $pracownik->start(); for ($i = 0; $i stack(new Task($i)); ) while ($worker->collect()); $pracownik->zamknięcie();

W powyższym przykładzie 15 zadań dla nowego obiektu $worker jest wypychanych na stos za pomocą metody Worker::stack, a następnie są przetwarzane w kolejności, w jakiej zostały wypchnięte. Metoda Worker::collect, jak pokazano powyżej, służy do czyszczenia zadań po zakończeniu ich wykonywania. Dzięki niemu w pętli while blokujemy główny wątek do czasu zakończenia i wyczyszczenia wszystkich zadań na stosie - zanim wywołamy Worker::shutdown . Wczesne zakończenie procesu roboczego (tj. gdy nadal istnieją zadania do wykonania) nadal będzie blokować główny wątek do czasu zakończenia wykonywania wszystkich zadań, tyle że zadania nie zostaną usunięte (co oznacza wycieki pamięci).

Klasa Worker udostępnia kilka innych metod związanych ze stosem zadań, w tym Worker::unstack do usuwania ostatniego zadania na stosie i Worker::getStacked do pobierania liczby zadań na stosie wykonawczym. Stos pracownika zawiera tylko te zadania, które należy wykonać. Gdy zadanie na stosie zostanie ukończone, jest ono usuwane i umieszczane na osobnym (wewnętrznym) stosie w celu usunięcia śmieci (przy użyciu metody Worker::collect).

Innym sposobem ponownego wykorzystania wątku w wielu zadaniach jest użycie puli wątków (za pośrednictwem klasy Pool). Pula wątków wykorzystuje grupę procesów roboczych, aby umożliwić wykonanie zadań jednocześnie, w którym współczynnik współbieżności (liczba wątków puli, z którymi współpracuje) jest ustawiany podczas tworzenia puli.

Dostosujmy powyższy przykład do wykorzystania puli pracowników:

Klasa Zadanie rozszerza Wątkowe ( private $value; funkcja publiczna __construct(int $i) ( $this->value = $i; ) funkcja publiczna run() ( usleep(250000); echo "Zadanie: ($this->value) \n"; ) ) $pula = nowa Pula(4); for ($i = 0; $i przesyłam(nowe zadanie($i)); ) while ($pool->collect()); $pool->zamknięcie();

Istnieje kilka znaczących różnic w przypadku korzystania z basenu i pracownika. Po pierwsze, puli nie trzeba uruchamiać ręcznie; rozpoczyna wykonywanie zadań, gdy tylko staną się dostępne. Po drugie, my wysłać zadania na basen, nie połóż je na stosie. Ponadto klasa Pool nie dziedziczy po Threaded i dlatego nie może być przekazywana do innych wątków (w przeciwieństwie do Workera).

Dobrą praktyką jest, aby pracownicy i pule zawsze sprzątały swoje zadania zaraz po ich ukończeniu, a następnie samodzielnie je kończyły. Wątki utworzone przy użyciu klasy Thread muszą być również dołączone do wątku nadrzędnego.

wątki i (nie)zmienność

Ostatnią klasą, którą poruszymy, jest Volatile, nowy dodatek do pthreads v3. Niezmienność stała się ważną koncepcją w pthreads, ponieważ bez niej wydajność znacznie spada. Dlatego domyślnie właściwości klas Threaded, które same są obiektami Threaded, są teraz niezmienne i dlatego nie można ich zastąpić po ich początkowym przypisaniu. Obecnie preferowana jest jawna zmienność takich właściwości i nadal można ją osiągnąć przy użyciu nowej klasy Volatile.

Spójrzmy na przykład, który zademonstruje nowe ograniczenia niezmienności:

Zadanie klasowe rozszerza Threaded // klasę Threaded (funkcja publiczna __construct() ( $this->data = new Threaded(); // $this->data nie można zastąpić, ponieważ jest to właściwość Threaded klasy Threaded) ) $task = nowa klasa (new Task()) rozszerza wątek ( // klasę wątkową, ponieważ wątek rozszerza wątkową funkcję publiczną __construct($tm) ( $this->threadedMember = $tm; var_dump($this->threadedMember-> data); // obiekt(Threaded)#3 (0) () $this->threadedMember = new StdClass(); // niepoprawny, ponieważ właściwość jest elementem Threaded klasy Threaded ) );

Z drugiej strony, wątkowe właściwości klas Volatile można modyfikować:

Zadanie klasowe rozszerza się Volatile ( funkcja publiczna __construct() ( $this->data = new Threaded(); $this->data = new StdClass(); // poprawne, ponieważ jesteśmy w klasie niestabilnej) ) $task = new class(new Task()) rozszerza wątek ( funkcja publiczna __construct($vm) ( $this->volatileMember = $vm; var_dump($this->volatileMember->data); // obiekt(stdClass)#4 (0) () // nadal niepoprawny, ponieważ Volatile rozszerza Threaded, więc właściwość jest nadal członkiem Threaded klasy Threaded $this->volatileMember = new StdClass() ) );

Widzimy, że klasa Volatile zastępuje niezmienność narzuconą przez jej nadrzędną klasę Threaded, aby zapewnić możliwość zmiany właściwości Threaded (a także ich unset()).

Istnieje inny temat dyskusji dotyczący zmienności i klasy Volatile - tablice. W pthreads tablice są automatycznie rzutowane na obiekty Volatile po przypisaniu do właściwości klasy Threaded. Dzieje się tak dlatego, że manipulowanie tablicą wielu kontekstów PHP po prostu nie jest bezpieczne.

Spójrzmy jeszcze raz na przykład, aby lepiej zrozumieć niektóre rzeczy:

$tablica = ; $zadanie = nowa klasa($tablica) rozszerza wątek ( prywatne $dane; funkcja publiczna __construct(array $array) ( $this->data = $array; ) funkcja publiczna run() ( $this->data = 4; $ to->dane = 5; print_r($to->dane) ); $zadanie->start() && $zadanie->dołącz(); /* Dane wyjściowe: Obiekt niestabilny ( => 1 => 2 => 3 => 4 => 5) */

Widzimy, że obiekty Volatile można traktować tak, jakby były tablicami, ponieważ obsługują operacje na tablicach, takie jak (jak pokazano powyżej) operator subset(). Jednak klasy Volatile nie obsługują podstawowych funkcji tablicowych, takich jak array_pop i array_shift. Zamiast tego klasa Threaded udostępnia nam podobne operacje, jak metody wbudowane.

Jako demonstracja:

$data = nowa klasa rozszerza Volatile ( public $a = 1; public $b = 2; public $c = 3; ); var_dump($dane); var_dump($data->pop()); var_dump($data->shift()); var_dump($dane); /* Dane wyjściowe: obiekt(klasa@anonimowy)#1 (3) ( ["a"]=> int(1) ["b"]=> int(2) ["c"]=> int(3) ) int(3) int(1) obiekt(klasa@anonymous)#1 (1) ( ["b"]=> int(2) ) */

Inne obsługiwane operacje obejmują Threaded::chunk i Threaded::merge .

Synchronizacja

W ostatniej części tego artykułu przyjrzymy się synchronizacji w pwątkach. Synchronizacja to metoda pozwalająca kontrolować dostęp do udostępnionych zasobów.

Na przykład zaimplementujmy prosty licznik:

$counter = nowa klasa rozszerza wątek (public $i = 0; funkcja publiczna run() ( for ($i = 0; $i i; ) ) ); $licznik->start(); for ($i = 0; $i i; ) $counter->join(); var_dump($licznik->i); // wypisze liczbę od 10 do 20

Bez użycia synchronizacji wynik nie jest deterministyczny. Wiele wątków zapisuje do jednej zmiennej bez kontrolowany dostęp, co oznacza, że ​​aktualizacje zostaną utracone.

Naprawmy to, aby uzyskać poprawny wynik 20, dodając czas:

$counter = nowa klasa rozszerza wątek ( public $i = 0; funkcja publiczna run() ( $this->synchronized(function () ( for ($i = 0; $i i; ) )); ) ); $licznik->start(); $licznik->synchronized(funkcja ($licznik) ( for ($i = 0; $i i; ) ), $licznik); $przeciwnik->dołącz(); var_dump($licznik->i); //int(20)

Zsynchronizowane bloki kodu mogą także komunikować się ze sobą przy użyciu metod Threaded::wait i Threaded::notify (lub Threaded::notifyAll).

Oto sekwencyjny przyrost w dwóch zsynchronizowanych podczas gdy pętle:

$counter = nowa klasa rozszerza wątek ( public $cond = 1; publiczna funkcja run() ( $this->synchronized(function () ( for ($i = 0; $i notify(); if ($this->cond === 1) ( $this->cond = 2; $this->wait(); ) ) )); $licznik->start(); $licznik->synchronized(function ($licznik) ( if ($licznik->warunek !== 2) ( $licznik->wait(); // czekaj dla inny, aby rozpocząć jako pierwszy) for ($i = 10; $i notify(); if ($counter->cond === 2) ( $counter->cond = 1; $counter->wait(); ) ) ) , $licznik); $przeciwnik->dołącz(); /* Dane wyjściowe: 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) */

Możesz to zauważyć dodatkowe warunki, które zostały umieszczone wokół wywołania Threaded::wait . Warunki te są krytyczne, ponieważ umożliwiają wznowienie zsynchronizowanego wywołania zwrotnego po otrzymaniu powiadomienia i spełnieniu określonego warunku. Jest to ważne, ponieważ powiadomienia mogą pochodzić z innych miejsc niż wywołanie Threaded::notify. Zatem, jeśli wywołania metody Threaded::wait nie zostały ujęte w warunki, wykonamy je fałszywe pobudki, co doprowadzi do nieprzewidywalnego zachowania kodu.

Wniosek

Przyjrzeliśmy się pięciu klasom pakietu pthreads (Threaded, Thread, Worker, Volatile i Pool) oraz sposobom użycia każdej klasy. Przyjrzeliśmy się także nowej koncepcji niezmienności pthreads, stworzonej krótki przegląd obsługiwane możliwości synchronizacji. Mając te podstawy, możemy teraz zacząć przyglądać się, jak pthreads można wykorzystać w rzeczywistych przypadkach! To będzie temat naszego kolejnego wpisu.

Jeśli jesteś zainteresowany tłumaczeniem kolejnego wpisu, daj znać: skomentuj w mediach społecznościowych. sieci, zagłosuj i udostępnij post współpracownikom i znajomym.

  • Programowanie,
  • Programowanie równoległe
  • Niedawno wypróbowałem pthreads i byłem mile zaskoczony - jest to rozszerzenie, które dodaje do PHP możliwość pracy z wieloma rzeczywistymi wątkami. Żadnej emulacji, żadnej magii, żadnych podróbek – wszystko jest prawdziwe.



    Rozważam takie zadanie. Jest cała masa zadań, które trzeba szybko wykonać. PHP ma inne narzędzia do rozwiązania tego problemu, nie są one tutaj wymienione, artykuł dotyczy pthreads.



    Co to są pthreads

    To wszystko! No prawie wszystko. Rzeczywiście jest coś, co może zdenerwować dociekliwego czytelnika. To wszystko nie działa standardowe PHP, skompilowany z opcjami domyślnymi. Aby cieszyć się wielowątkowością, musisz mieć włączoną funkcję ZTS (Zend Thread Safety) w swoim PHP.

    Konfiguracja PHP

    Następnie PHP z ZTS. Nie zwracajcie uwagi na tak dużą różnicę w czasie wykonania w porównaniu do PHP bez ZTS (37,65 vs 265,05 sekundy), nie próbowałem sprowadzić tego do wspólnego mianownika Ustawienia PHP. W przypadku bez ZTS mam włączony np. XDebug.


    Jak widać przy zastosowaniu 2 wątków prędkość wykonania programu jest około 1,5 razy większa niż w przypadku kodu liniowego. Przy użyciu 4 wątków - 3 razy.


    Można zauważyć, że mimo że procesor jest 8-rdzeniowy, czas wykonania programu pozostał prawie niezmieniony, jeśli wykorzystano więcej niż 4 wątki. Wydaje się, że wynika to z faktu, że mój procesor ma 4 rdzenie fizyczne. Dla przejrzystości płytkę przedstawiłem w formie schematu.


    Wznawiać

    W PHP możliwa jest całkiem elegancka praca z wielowątkowością przy użyciu rozszerzenia pthreads. Daje to zauważalny wzrost produktywności.

    Tagi:

    • php
    • p-wątki
    Dodaj tagi

    Czasami konieczne staje się wykonanie kilku czynności jednocześnie, na przykład sprawdzenie zmian w jednej tabeli bazy danych i dokonanie modyfikacji w drugiej. Co więcej, jeśli jedna z operacji (na przykład sprawdzenie zmian) zajmuje dużo czasu, oczywiste jest, że wykonanie sekwencyjne nie zapewni równoważenia zasobów.

    Aby rozwiązać tego typu problem, programowanie wykorzystuje wielowątkowość - każda operacja umieszczana jest w osobnym wątku z przydzieloną ilością zasobów i działa w jego obrębie. Dzięki takiemu podejściu wszystkie zadania zostaną zrealizowane osobno i niezależnie.

    Chociaż PHP nie obsługuje wielowątkowości, istnieje kilka metod jego emulacji, o których mowa porozmawiamy poniżej.

    1. Uruchomienie kilku kopii skryptu – jedna kopia na operację

    //woman.php if (!isset($_GET["wątek"])) ( system("wget ​​​​http://localhost/woman.php?thread=make_me_happy"); system("wget ​​http: //localhost/ kobieta.php?thread=make_me_rich"); ) elseif ($_GET["wątek"] == "make_me_happy") ( make_her_happy(); ) elseif ($_GET["wątek"] == "make_me_happy" ) ( znajdź_innego_jeden( ; )

    Kiedy wykonamy ten skrypt bez parametrów, automatycznie uruchomi się on w dwóch kopiach z identyfikatorami operacji („thread=make_me_happy” i „thread=make_me_rich”), które inicjują wykonanie niezbędnych funkcji.

    W ten sposób osiągamy zamierzony efekt - dwie operacje są wykonywane jednocześnie - ale to oczywiście nie jest wielowątkowość, ale po prostu kula do jednoczesnego wykonywania zadań.

    2. Ścieżka Jedi - z wykorzystaniem rozszerzenia PCNTL

    PCNTL to rozszerzenie, które pozwala w pełni pracować z procesami. Oprócz zarządzania wspiera wysyłanie wiadomości, sprawdzanie statusów i ustalanie priorytetów. Tak wygląda poprzedni skrypt wykorzystujący PCNTL:

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

    Wygląda to dość zagmatwanie, przejrzyjmy to linia po linii.

    W pierwszej linii „forkujemy” bieżący proces (fork kopiuje proces z zachowaniem wartości wszystkich zmiennych), dzieląc go na dwa procesy (bieżący i potomny) działające równolegle.

    Aby zrozumieć, w jakim punkcie jesteśmy w tej chwili w procesie potomnym lub macierzystym funkcja pcntl_fork zwraca 0 dla dziecka i identyfikator procesu dla matki. Dlatego w drugiej linii patrzymy na $pid, jeśli wynosi zero, to jesteśmy w procesie potomnym - wykonujemy funkcję, w innym przypadku jesteśmy w matce (linia 4), po czym tworzymy kolejny proces i podobnie wykonaj zadanie.

    Proces wykonania skryptu:

    Tym samym skrypt tworzy jeszcze 2 procesy potomne, które są jego kopiami i zawierają te same zmienne o podobnych wartościach. A korzystając z identyfikatora zwróconego przez funkcję pcntl_fork, dowiadujemy się, w którym wątku aktualnie się znajdujemy i wykonujemy niezbędne działania.



    Niedawno wypróbowałem pthreads i byłem mile zaskoczony - jest to rozszerzenie, które dodaje do PHP możliwość pracy z wieloma rzeczywistymi wątkami. Żadnej emulacji, żadnej magii, żadnych podróbek – wszystko jest prawdziwe.



    Rozważam takie zadanie. Jest cała masa zadań, które trzeba szybko wykonać. PHP ma inne narzędzia do rozwiązania tego problemu, nie są one tutaj wymienione, artykuł dotyczy pthreads.



    Co to są pthreads

    To wszystko! No prawie wszystko. Rzeczywiście jest coś, co może zdenerwować dociekliwego czytelnika. Nic z tego nie działa na standardowym PHP skompilowanym z opcjami domyślnymi. Aby cieszyć się wielowątkowością, musisz mieć włączoną funkcję ZTS (Zend Thread Safety) w swoim PHP.

    Konfiguracja PHP

    Następnie PHP z ZTS. Nie zwracajcie uwagi na tak dużą różnicę w czasie wykonania w porównaniu do PHP bez ZTS (37,65 vs 265,05 sekundy), nie próbowałem uogólniać ustawień PHP. W przypadku bez ZTS mam włączony np. XDebug.


    Jak widać przy zastosowaniu 2 wątków prędkość wykonania programu jest około 1,5 razy większa niż w przypadku kodu liniowego. Przy użyciu 4 wątków - 3 razy.


    Można zauważyć, że mimo że procesor jest 8-rdzeniowy, czas wykonania programu pozostał prawie niezmieniony, jeśli wykorzystano więcej niż 4 wątki. Wydaje się, że wynika to z faktu, że mój procesor ma 4 rdzenie fizyczne. Dla przejrzystości płytkę przedstawiłem w formie schematu.


    Wznawiać

    W PHP możliwa jest całkiem elegancka praca z wielowątkowością przy użyciu rozszerzenia pthreads. Daje to zauważalny wzrost produktywności.

    Niedawno wypróbowałem pthreads i byłem mile zaskoczony - jest to rozszerzenie, które dodaje do PHP możliwość pracy z wieloma rzeczywistymi wątkami. Żadnej emulacji, żadnej magii, żadnych podróbek – wszystko jest prawdziwe.



    Rozważam takie zadanie. Jest cała masa zadań, które trzeba szybko wykonać. PHP ma inne narzędzia do rozwiązania tego problemu, nie są one tutaj wymienione, artykuł dotyczy pthreads.



    Co to są pthreads

    To wszystko! No prawie wszystko. Rzeczywiście jest coś, co może zdenerwować dociekliwego czytelnika. Nic z tego nie działa na standardowym PHP skompilowanym z opcjami domyślnymi. Aby cieszyć się wielowątkowością, musisz mieć włączoną funkcję ZTS (Zend Thread Safety) w swoim PHP.

    Konfiguracja PHP

    Następnie PHP z ZTS. Nie zwracajcie uwagi na tak dużą różnicę w czasie wykonania w porównaniu do PHP bez ZTS (37,65 vs 265,05 sekundy), nie próbowałem uogólniać ustawień PHP. W przypadku bez ZTS mam włączony np. XDebug.


    Jak widać przy zastosowaniu 2 wątków prędkość wykonania programu jest około 1,5 razy większa niż w przypadku kodu liniowego. Przy użyciu 4 wątków - 3 razy.


    Można zauważyć, że mimo że procesor jest 8-rdzeniowy, czas wykonania programu pozostał prawie niezmieniony, jeśli wykorzystano więcej niż 4 wątki. Wydaje się, że wynika to z faktu, że mój procesor ma 4 rdzenie fizyczne. Dla przejrzystości płytkę przedstawiłem w formie schematu.


    Wznawiać

    W PHP możliwa jest całkiem elegancka praca z wielowątkowością przy użyciu rozszerzenia pthreads. Daje to zauważalny wzrost produktywności.

    Tagi: Dodaj tagi



    Jeśli zauważysz błąd, zaznacz fragment tekstu i naciśnij Ctrl+Enter
    UDZIAŁ: