Współbieżność a równoległość

Zarówno współbieżność, jak i równoległość są używane w odniesieniu do programów wielowątkowych, ale istnieje wiele nieporozumień dotyczących podobieństwa i różnicy między nimi. Najważniejsze pytanie w tym zakresie: czy równoległość współbieżności jest czy nie? Chociaż oba terminy wydają się dość podobne, ale odpowiedź na powyższe pytanie brzmi NIE, współbieżność i równoległość nie są tym samym. A jeśli nie są takie same, jaka jest między nimi podstawowa różnica?

Mówiąc prościej, współbieżność zajmuje się zarządzaniem dostępem do współdzielonego stanu z różnych wątków, a z drugiej strony równoległość polega na wykorzystaniu wielu procesorów lub ich rdzeni w celu poprawy wydajności sprzętu.

Szczegóły współbieżności

Współbieżność występuje, gdy dwa zadania nakładają się na siebie podczas wykonywania. Może to być sytuacja, w której aplikacja obsługuje więcej niż jedno zadanie w tym samym czasie. Możemy to zrozumieć schematycznie; w tym samym czasie postępuje wiele zadań, jak następuje -

Poziomy współbieżności

W tej sekcji omówimy trzy ważne poziomy współbieżności w zakresie programowania -

Współbieżność niskiego poziomu

Na tym poziomie współbieżności istnieje jawne użycie operacji atomowych. Nie możemy używać tego rodzaju współbieżności do budowania aplikacji, ponieważ jest ona bardzo podatna na błędy i trudna do debugowania. Nawet Python nie obsługuje tego rodzaju współbieżności.

Współbieżność średniego poziomu

W tej współbieżności nie ma użycia jawnych operacji atomowych. Używa jawnych blokad. Python i inne języki programowania obsługują tego rodzaju współbieżność. Przeważnie programiści aplikacji używają tej współbieżności.

Współbieżność wysokiego poziomu

W tej współbieżności nie są używane ani jawne operacje niepodzielne, ani jawne blokady. Python maconcurrent.futures moduł do obsługi tego rodzaju współbieżności.

Właściwości systemów współbieżnych

Aby program lub system współbieżny były poprawne, muszą spełniać pewne właściwości. Właściwości związane z zakończeniem systemu są następujące -

Właściwość poprawności

Właściwość poprawności oznacza, że ​​program lub system musi podać żądaną poprawną odpowiedź. Aby to uprościć, możemy powiedzieć, że system musi poprawnie odwzorować stan programu początkowego na stan końcowy.

Majątek bezpieczeństwa

Właściwość bezpieczeństwa oznacza, że ​​program lub system musi pozostać w pliku “good” lub “safe” stan i nigdy nic nie robi “bad”.

Majątek życia

Ta właściwość oznacza, że ​​program lub system musi “make progress” i osiągnie jakiś pożądany stan.

Aktorzy systemów współbieżnych

Jest to wspólna właściwość systemu współbieżnego, w którym może istnieć wiele procesów i wątków, które działają w tym samym czasie, aby wykonywać postępy w wykonywaniu własnych zadań. Te procesy i wątki nazywane są aktorami systemu współbieżnego.

Zasoby systemów współbieżnych

Aktorzy muszą wykorzystywać zasoby, takie jak pamięć, dysk, drukarka itp., Aby wykonywać swoje zadania.

Pewien zbiór zasad

Każdy współbieżny system musi posiadać zestaw reguł określających rodzaj zadań do wykonania przez aktorów i ich harmonogram. Zadania mogą obejmować pozyskiwanie blokad, udostępnianie pamięci, modyfikowanie stanu itp.

Bariery współbieżnych systemów

Wdrażając systemy współbieżne, programista musi wziąć pod uwagę dwa ważne zagadnienia, które mogą stanowić bariery systemów współbieżnych -

Udostępnianie danych

Istotną kwestią podczas wdrażania systemów współbieżnych jest współdzielenie danych pomiędzy wieloma wątkami lub procesami. W rzeczywistości programista musi zapewnić, że blokady chronią współdzielone dane, tak aby wszystkie dostępy do nich były serializowane i tylko jeden wątek lub proces miał dostęp do współdzielonych danych w danym momencie. W przypadku, gdy wiele wątków lub procesów próbuje uzyskać dostęp do tych samych udostępnionych danych, nie wszystkie, z wyjątkiem co najmniej jednego, zostaną zablokowane i pozostaną bezczynne. Innymi słowy, możemy powiedzieć, że bylibyśmy w stanie używać tylko jednego procesu lub wątku w czasie, gdy obowiązuje blokada. Istnieje kilka prostych rozwiązań w celu usunięcia wyżej wymienionych barier -

Ograniczenie udostępniania danych

Najprostszym rozwiązaniem jest nieudostępnianie żadnych zmiennych danych. W takim przypadku nie musimy używać jawnego blokowania, a bariera współbieżności spowodowana wzajemnymi danymi zostałaby rozwiązana.

Pomoc w zakresie struktury danych

Często współbieżne procesy muszą mieć dostęp do tych samych danych w tym samym czasie. Innym rozwiązaniem, niż użycie jawnych blokad, jest użycie struktury danych, która obsługuje współbieżny dostęp. Na przykład możemy użyćqueuemoduł, który zapewnia kolejki bezpieczne dla wątków. Możemy również użyćmultiprocessing.JoinableQueue klasy dla współbieżności opartej na wieloprocesowości.

Niezmienny transfer danych

Czasami struktura danych, której używamy, powiedzmy kolejka współbieżności, nie jest odpowiednia, wtedy możemy przekazać niezmienne dane bez ich blokowania.

Zmienny transfer danych

Kontynuując powyższe rozwiązanie, załóżmy, że jeśli wymagane jest przekazywanie tylko danych podlegających zmianom, a nie danych niezmiennych, to możemy przekazywać zmienne dane, które są tylko do odczytu.

Udostępnianie zasobów we / wy

Inną ważną kwestią we wdrażaniu systemów współbieżnych jest wykorzystanie zasobów we / wy przez wątki lub procesy. Problem pojawia się, gdy jeden wątek lub proces używa wejścia / wyjścia przez tak długi czas, a inny jest bezczynny. Tego rodzaju barierę widzimy pracując z aplikacją wymagającą dużej liczby wejść / wyjść. Można to zrozumieć na przykładzie żądania stron z przeglądarki internetowej. To ciężka aplikacja. Tutaj, jeśli szybkość, z jaką żądane są dane, jest wolniejsza niż szybkość, z jaką są one zużywane, to mamy barierę we / wy w naszym współbieżnym systemie.

Poniższy skrypt Pythona służy do żądania strony internetowej i uzyskania czasu, jaki zajęło naszej sieci uzyskanie żądanej strony -

import urllib.request

import time

ts = time.time()

req = urllib.request.urlopen('http://www.tutorialspoint.com')

pageHtml = req.read()

te = time.time()

print("Page Fetching Time : {} Seconds".format (te-ts))

Po wykonaniu powyższego skryptu możemy uzyskać czas pobierania strony, jak pokazano poniżej.

Wynik

Page Fetching Time: 1.0991398811340332 Seconds

Widzimy, że czas na pobranie strony jest dłuższy niż jedna sekunda. A co, jeśli chcemy pobrać tysiące różnych stron internetowych, możesz zrozumieć, ile czasu zajmie nasza sieć.

Co to jest równoległość?

Równoległość można zdefiniować jako sztukę dzielenia zadań na podzadania, które mogą być przetwarzane jednocześnie. Jest to przeciwieństwo współbieżności, jak omówiono powyżej, w której dwa lub więcej zdarzeń zachodzi w tym samym czasie. Możemy to zrozumieć schematycznie; zadanie jest podzielone na kilka podzadań, które mogą być przetwarzane równolegle, w następujący sposób -

Aby uzyskać więcej informacji na temat różnicy między współbieżnością a równoległością, rozważ następujące punkty -

Współbieżne, ale nie równoległe

Aplikacja może być współbieżna, ale nie równoległa, co oznacza, że ​​w tym samym czasie przetwarza więcej niż jedno zadanie, ale zadania nie są dzielone na podzadania.

Równolegle, ale nie równolegle

Aplikacja może być równoległa, ale nie współbieżna, co oznacza, że ​​działa tylko na jednym zadaniu na raz, a zadania podzielone na podzadania mogą być przetwarzane równolegle.

Ani równoległe, ani współbieżne

Aplikacja nie może być ani równoległa, ani współbieżna. Oznacza to, że działa tylko na jednym zadaniu naraz, a zadanie nigdy nie jest dzielone na podzadania.

Zarówno równoległe, jak i współbieżne

Aplikacja może być zarówno równoległa, jak i współbieżna, co oznacza, że ​​działa jednocześnie na wielu zadaniach, a zadanie jest podzielone na podzadania do wykonywania ich równolegle.

Konieczność równoległości

Możemy osiągnąć paralelizm, rozdzielając podzadania na różne rdzenie pojedynczego procesora lub na wiele komputerów połączonych w sieci.

Rozważ następujące ważne punkty, aby zrozumieć, dlaczego konieczne jest osiągnięcie równoległości:

Wydajne wykonanie kodu

Za pomocą równoległości możemy wydajnie uruchamiać nasz kod. Zaoszczędzi to nasz czas, ponieważ ten sam kod w częściach działa równolegle.

Szybszy niż przetwarzanie sekwencyjne

Obliczenia sekwencyjne są ograniczone czynnikami fizycznymi i praktycznymi, przez co nie jest możliwe uzyskanie szybszych wyników obliczeń. Z drugiej strony ten problem jest rozwiązany przez obliczenia równoległe i daje nam szybsze wyniki obliczeń niż obliczenia sekwencyjne.

Krótszy czas realizacji

Przetwarzanie równoległe skraca czas wykonywania kodu programu.

Jeśli mówimy o prawdziwym przykładzie równoległości, karta graficzna naszego komputera jest przykładem, który podkreśla prawdziwą moc przetwarzania równoległego, ponieważ ma setki indywidualnych rdzeni przetwarzających, które działają niezależnie i mogą wykonywać operacje w tym samym czasie. Z tego powodu jesteśmy w stanie uruchamiać również aplikacje i gry z najwyższej półki.

Zrozumienie procesorów do wdrożenia

Wiemy o współbieżności, równoległości i różnicy między nimi, ale co z systemem, w którym ma być zaimplementowana. Niezbędna jest znajomość systemu, na którym będziemy wdrażać, ponieważ daje nam to możliwość podejmowania świadomych decyzji podczas projektowania oprogramowania. Mamy dwa rodzaje procesorów -

Procesory jednordzeniowe

Procesory jednordzeniowe mogą w dowolnym momencie wykonywać jeden wątek. Te procesory używającontext switchingaby przechowywać wszystkie niezbędne informacje dotyczące wątku w określonym czasie, a następnie przywrócić je później. Mechanizm przełączania kontekstów pomaga nam robić postępy w wielu wątkach w ciągu danej sekundy i wygląda na to, że system pracuje nad wieloma rzeczami.

Procesory jednordzeniowe mają wiele zalet. Te procesory wymagają mniej energii i nie ma złożonego protokołu komunikacyjnego między wieloma rdzeniami. Z drugiej strony szybkość procesorów jednordzeniowych jest ograniczona i nie nadaje się do większych aplikacji.

Procesory wielordzeniowe

Procesory wielordzeniowe mają wiele niezależnych jednostek przetwarzających zwanych również cores.

Takie procesory nie potrzebują mechanizmu przełączania kontekstu, ponieważ każdy rdzeń zawiera wszystko, czego potrzebuje do wykonania sekwencji zapisanych instrukcji.

Cykl pobierania, dekodowania i wykonywania

Rdzenie procesorów wielordzeniowych wykonują cykl. Ten cykl nazywa sięFetch-Decode-Executecykl. Obejmuje następujące kroki -

Sprowadzać

Jest to pierwszy krok cyklu, który polega na pobieraniu instrukcji z pamięci programu.

Rozszyfrować

Niedawno pobrane instrukcje byłyby konwertowane na serię sygnałów, które będą wyzwalać inne części procesora.

Wykonać

Jest to ostatni krok, w którym zostaną wykonane pobrane i zdekodowane instrukcje. Wynik wykonania zostanie zapisany w rejestrze procesora.

Jedną z zalet jest to, że wykonanie w procesorach wielordzeniowych jest szybsze niż w procesorach jednordzeniowych. Nadaje się do większych zastosowań. Z drugiej strony problemem jest złożony protokół komunikacyjny między wieloma rdzeniami. Wiele rdzeni wymaga więcej mocy niż procesory jednordzeniowe.