logo

WIELOWĄTKOWOŚĆ W C

Wstęp:

W języku C termin „wielowątkowość” opisuje użycie wielu wątki jednocześnie. Każdy wątek wykonuje inne zadanie . Ze względu na współbieżny charakter wielowątkowości wiele zadań może być realizowanych jednocześnie. Dodatkowo, wielowątkowość zmniejsza Wykorzystanie zasobów procesora . Istnieją dwie kategorie wielozadaniowości: oparte na procesie I oparte na wątkach . Kiedy cokolwiek jest określane jako wielowątkowość, oznacza to, że w tym samym procesie działają co najmniej dwa lub więcej wątków jednocześnie. Musimy najpierw zrozumieć, czym jest wątek i proces, aby zrozumieć wielowątkowość w C. Przyjrzyjmy się tym tematom, aby lepiej zrozumieć.

listy lateksowe

Czym są procesy i wątki?

A nitka jest zasadniczy budynek blok wykonania dowolnego procesu. Program składa się z kilku procesów, a każdy proces składa się z wątków, które są znacznie bardziej podstawowymi jednostkami. Dlatego wątek można uznać za podstawowy element składowy procesu lub prostszą jednostkę, która wspólnie określa wykorzystanie procesora.

Wątek zawiera następujące elementy:

Identyfikator wątku:

To coś specjalnego identyfikator wątku generowany w momencie tworzenia wątku i zachowywany przez czas trwania tego konkretnego wątku.

Licznik programu:

Jest to wartość, którą obciążenia sprzętowe .

Zarejestrowany zestaw:

Jest to zbiór wspólne rejestry .

Stos:

To pozostałość po tym konkretny wątek .

Ponadto, jeśli w tym samym procesie dwa wątki pracują jednocześnie, dzielą się one kod , sekcje danych i inne zasoby systemu operacyjnego, takie jak plik otwiera się I sygnały . Proces o dużej wadze, rodzaj procesu konwencjonalnego, może kontrolować jeden wątek. Jednakże wielowątkowa kontrola może otwierać i wykonywać wiele zadań jednocześnie. Dzięki zastosowaniu wątków system staje się znacznie efektywniejszy, dlatego są one przydatne.

Rozróżnienie pomiędzy pojedynczy I wielowątkowość w C jest wyjaśnione. Przede wszystkim jest to A proces jednowątkowy . W efekcie cały blok – łącznie z kod, dane, itp. - jest uważany za jeden proces, a proces ten ma tylko jeden wątek. Oznacza to, że ta technika wykona tylko jedno zadanie na raz. Ale jest proces wielowątkowy co stoi w opozycji do tego. Są takie zajęcia jak kod, stos, dane , I akta również, ale są one wykonywane przez kilka wątków, z których każdy ma swój własny stos i rejestry. Biorąc pod uwagę, że w tej sytuacji wiele zadań można wykonać jednocześnie, proces ten nazywa się a proces wielowątkowy .

Nici występują w dwóch odmianach:

Wątek na poziomie użytkownika:

Dzieje się to na poziomie użytkownika, jak sugeruje nazwa. Jądro nie ma dostępu do swoich danych.

Wątek na poziomie jądra

Rodzaj wątku odnosi się do związku wątku z jądrem i systemem operacyjnym systemu.

Proces- Seria kroków podejmowanych w celu realizacji programu może być określana jako proces . Program nie jest wykonywany natychmiast po uruchomieniu. Jest on podzielony na kilka podstawowych kroków, które są wykonywane sekwencyjnie w zorganizowany sposób, aby ostatecznie doprowadzić do wykonania procesu.

tablica obiektów w Javie

Proces podzielony na mniejsze etapy nazywa się a „klon lub proces potomny”, podczas gdy oryginalny proces jest określany jako proces „rodzicielski”. . W pamięci każdy proces zajmuje pewną ilość miejsca, która nie jest współdzielona z żadnymi innymi procesami.

Procedura przed wykonaniem przechodzi przez kilka etapów.

NOWY-

W tej sytuacji nowy proces jest wygenerowane .

GOTOWY-

Kiedy proces jest przygotowany i czeka na przypisanie procesora, znajduje się w tym stanie.

DZIAŁANIE-

Kiedy proces jest aktywny, jest to stan.

CZEKANIE-

Kiedy proces jest w tym stanie, coś jest Czekanie wydarzyć się.

ZAKOŃCZONY-

Jest to stan, w jakim przeprowadzana jest procedura.

Dlaczego C jest wielowątkowy?

Wielowątkowość w koncepcji C można wykorzystać poprzez równoległość w celu ulepszenia funkcjonalność aplikacji . Rozważmy przypadek, gdy w oknie przeglądarki otwartych jest kilka kart. Następnie każda zakładka działa jednocześnie i można ją nazwać a Nitka . Zakładając, że używamy Microsoft Excel , wystarczy jeden wątek formatowanie tekstu i jeden wątek będzie obsługiwać wejście . Dlatego funkcja wielowątkowości języka C ułatwia wykonywanie wielu zadań jednocześnie. Tworzenie wątku jest znacznie szybsze. Transfer kontekstu między wątkami odbywa się szybciej. Ponadto komunikacja między wątkami może być szybsza, a kończenie wątków jest proste.

Jak pisać programy w C dla wielowątkowości?

Chociaż aplikacje wielowątkowe nie są wbudowane w język C, jest to możliwe w zależności od systemu operacyjnego. The standardowa biblioteka threads.h służy do implementacji idei wielowątkowości w C . Jednak obecnie nie ma kompilatora, który mógłby to zrobić. Musimy zastosować implementacje specyficzne dla platformy, takie jak „POSIX” Threads, korzystając z pliku nagłówkowego pwątek.h , jeśli chcemy zastosować wielowątkowość w C. „Pwątki” to inna nazwa tego. A POSIX wątek można utworzyć na następujące sposoby:

 #include pthread_create (thread, attr, start_routine, arg) 

W tym przypadku, Pthread_create tworzy nowy wątek, aby wątek był wykonywalny. Pozwala zaimplementować wielowątkowość w C tyle razy, ile chcesz w swoim kodzie. Tutaj znajdują się parametry i ich opisy z wcześniejszych wersji.

nitka:

To jest pojedyncza identyfikacja że zwroty podprocesu .

atr:

Kiedy chcemy ustawić atrybuty wątku, używamy tego atrybut nieprzezroczysty .

start_procedura:

Gdy start_procedura zostanie wygenerowany, wątek wykona procedurę.

pełna forma ide

argument:

Parametr, który start_procedura otrzymuje. ZERO zostanie użyty, jeśli nie zostaną podane żadne argumenty.

Niektóre przykłady wielowątkowości w języku C

Oto kilka przykładów problemów z wielowątkowością w C.

1. Problem czytelnika-pisarza

Częstym problemem systemu operacyjnego związanym z synchronizacją procesów jest problem czytelnika/pisarza . Załóżmy, że mamy bazę danych Czytelnicy I Pisarze , dwie różne kategorie użytkowników, mają dostęp. Czytelnicy tylko oni mogą Czytać bazę danych, natomiast Pisarze tylko oni mogą czytać bazę danych i ją aktualizować. Użyjmy IRCTC jako prosty przykład. Jeśli chcemy sprawdzić status konkretnego numer pociągu wystarczy wpisać numer pociągu do systemu, aby wyświetlić odpowiednie informacje o pociągu. Tutaj wyświetlane są tylko te informacje, które są obecne na stronie internetowej. Operator odczytu jest taki. Jeśli jednak chcemy zarezerwować bilet, musimy wypełnić formularz rezerwacji biletu, podając takie dane, jak imię i nazwisko, wiek itp. Zatem przeprowadzimy tutaj operację zapisu. Wprowadzone zostaną pewne zmiany w Baza danych IRCTC .

Problem polega na tym, że kilka osób jednocześnie próbuje uzyskać dostęp do pliku Baza danych IRCTC . Mogą być pisarz lub czytelnik . Problem pojawia się, jeśli czytelnik już korzysta z bazy danych, a piszący uzyskuje do niej jednocześnie dostęp, aby pracować nad tymi samymi danymi. Inny problem pojawia się, gdy autor korzysta z bazy danych, a czytelnik uzyskuje dostęp do tych samych informacji, co baza danych. Po trzecie, pojawia się trudność, gdy jeden autor aktualizuje bazę danych, podczas gdy inny próbuje zaktualizować dane w tej samej bazie danych. Czwarty scenariusz ma miejsce, gdy dwóch czytelników próbuje odzyskać ten sam materiał. Wszystkie te problemy powstają, jeśli czytelnik i zapisujący korzystają z tych samych danych z bazy danych.

Semafor to metoda stosowana do rozwiązania tego problemu. Spójrzmy na ilustrację wykorzystania tego zagadnienia.

Proces czytnika:

 #include #include #include int rc = 0; // Reader count int data = 0; // Shared data pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_twrt = PTHREAD_COND_INITIALIZER; void* reader(void* arg) { int reader_id = *(int*)arg; pthread_mutex_lock(&mutex); rc++; if (rc == 1) pthread_cond_wait(&wrt, &mutex); pthread_mutex_unlock(&mutex); // Reading the shared data printf('Reader %d reads data: %d
&apos;, reader_id, data); pthread_mutex_lock(&amp;mutex); rc--; if (rc == 0) pthread_cond_signal(&amp;wrt); pthread_mutex_unlock(&amp;mutex); return NULL; } int main() { pthread_treaders[5]; // Assuming 5 reader threads int reader_ids[5]; for (int i = 0; i<5; i++) { reader_ids[i]="i" + 1; pthread_create(&readers[i], null, reader, &reader_ids[i]); } joining reader threads for (int i="0;" i< 5; pthread_join(readers[i], null); return 0; < pre> <p> <strong>Output:</strong> </p> <pre> Reader 1 reads data: 0 Reader 2 reads data: 0 Reader 3 reads data: 0 Reader 4 reads data: 0 Reader 5 reads data: 0 </pre> <p> <strong>Explanation:</strong> </p> <p>In this code, we have the shared variable data and the <strong> <em>reader count rc</em> </strong> . The <strong> <em>wrt condition</em> </strong> variable is used to limit access for the <strong> <em>writer process</em> </strong> , and the <strong> <em>mutex</em> </strong> is used to guarantee mutual exclusion for accessing the shared data.</p> <p>The reader process is represented by the <strong> <em>reader() function</em> </strong> . The <strong> <em>reader count (rc)</em> </strong> is increased before attaining the <strong> <em>mutex lock</em> </strong> . It uses <strong> <em>pthread_cond_wait()</em> </strong> to wait on the <strong> <em>wrt condition</em> </strong> variable if it is the <strong> <em>first reader (rc == 1)</em> </strong> . As a result, writers will be prevented from writing until all readers have completed.</p> <p>The reader process checks if it was the <strong> <em>last reader (rc == 0)</em> </strong> and lowers the reader <strong> <em>count (rc--)</em> </strong> after reading the shared data. If it was, <strong> <em>pthread_cond_signal()</em> </strong> signals the <strong> <em>wrt condition</em> </strong> variable to let waiting writer processes continue.</p> <p>Using the <strong> <em>pthread_create()</em> </strong> and <strong> <em>pthread_join() functions</em> </strong> , we <strong> <em>new</em> </strong> and <strong> <em>join</em> </strong> multiple reader threads in the <strong> <em>main() function</em> </strong> . An individual ID is assigned to each reader thread for identifying purposes.</p> <h3>Writer process:</h3> <pre> wait(wrt); . . WRITE INTO THE OBJECT . signal(wrt); </pre> <p>In the above example, same as the <strong> <em>reader process</em> </strong> , an operation known as the wait operation is carried out on <strong> <em>&apos;wrt&apos;</em> </strong> when a user wishes to access the data or object. After that, the new user won&apos;t be able to access the object. And once the user has finished writing, another signal operation is performed on <strong> <em>wrt</em> </strong> .</p> <h3>2. lock and unlock problem:</h3> <p>The idea of a <strong> <em>mutex</em> </strong> is utilized in multithreading in C to guarantee that there won&apos;t be a <strong> <em>race condition</em> </strong> between the <strong> <em>threads</em> </strong> . When multiple threads begin processing the same data at once, this circumstance is known as <strong> <em>racing</em> </strong> . However, if these circumstances exist, we must. We use the <strong> <em>mutex&apos;s lock()</em> </strong> and <strong> <em>unlock() functions</em> </strong> to secure a particular section of code for a specific thread. Such that, another thread cannot begin performing the same operation. The <strong> <em>&apos;critical section/region&apos;</em> </strong> is the name given to this protected code area. Before using the shared resources, we set up a lot in a certain area, and once we&apos;ve finished using them, we unlock them once more.</p> <p>Let&apos;s examine the operation of the mutex for locking and unlocking in multithreading in C:</p> <p> <strong>Example:</strong> </p> <pre> #include #include #include pthread_mutex_tmy_mutex = PTHREAD_MUTEX_INITIALIZER; int shared_data = 0; void *thread_function(void *arg) { pthread_mutex_lock(&amp;my_mutex); shared_data++; // Modify the shared data printf(&apos;Thread %ld: Shared data modified. New value: %d
&apos;, (long)arg, shared_data); pthread_mutex_unlock(&amp;my_mutex); return NULL; } int main() { pthread_tthreads[5]; // Assuming 5 threads for (int i = 0; i<5; i++) { if (pthread_create(&threads[i], null, thread_function, (void *)(long)(i + 1)) !="0)" fprintf(stderr, 'error creating thread %d
', i 1); return 1; } for (int i< 5; (pthread_join(threads[i], null) joining 0; < pre> <p> <strong>Output:</strong> </p> <pre> Thread 1: Shared data modified. New value: 1 Thread 2: Shared data modified. New value: 2 Thread 3: Shared data modified. New value: 3 Thread 4: Shared data modified. New value: 4 Thread 5: Shared data modified. New value: 5 </pre> <p> <strong>Explanation:</strong> </p> <p>In this above example, we explain how we <strong> <em>lock</em> </strong> and <strong> <em>unlock</em> </strong> a certain region of code that shields us from the racing situation. <strong> <em>&apos;pthread_mutex_t&apos;</em> </strong> is used as an <strong> <em>initializer</em> </strong> in the example above. <strong> <em>&apos;pthread_mutex_lock&apos;</em> </strong> is then <strong> <em>written</em> </strong> before the beginning of the code that we want to lock. The coding that we wish to lock is finished after that. After that, the locking of the code is terminated using <strong> <em>&apos;pthread_mutex_unlock&apos;</em> </strong> ; going forward, no code will be in lock mode.</p> <h2>The Dining Philosopher Problem:</h2> <p>One of the classic issues with synchronization is the <strong> <em>dining philosopher issue</em> </strong> . Simple resource allocation for several processes is required but shouldn&apos;t result in a <strong> <em>stalemate</em> </strong> or <strong> <em>hunger</em> </strong> . The <strong> <em>dining philosopher problem</em> </strong> can be viewed as a straightforward representation of a number of processes, each of which is demanding resources. Since each of these processes requires a resource allocation, it is necessary to distribute those resources across all of the processes so that no one process ever gets stuck or stops working.</p> <p>Assume there are five philosophers seated at a <strong> <em>circle-shaped table</em> </strong> . They eat at one point and ponder about something at another. Around the round table, the philosophers are evenly spaced out on the chairs. Additionally, there is a bowl of rice and five chopsticks for each philosopher in the middle of the table. When the philosopher feels she cannot interact with her colleagues who are seated nearby.</p> <p>A philosopher occasionally takes up two chopsticks when she becomes hungry. She chooses two chopsticks from her neighbors-one on her <strong> <em>left</em> </strong> and one on her <strong> <em>right</em> </strong> -that are within easy reach. But the philosopher should never pick up more than one chopstick at once. She will obviously be unable to pick up the chopstick that the neighbor is using.</p> <p> <strong>Example:</strong> </p> <p>Let&apos;s use an example to demonstrate how this is implemented in C.</p> <pre> #include #include #include #include #include pthread_tphilosopher[5]; pthread_mutex_tchopstick[5]; void *func(void *arg) { int n = *(int *)arg; printf(&apos;
Philosopher %d is thinking.&apos;, n); pthread_mutex_lock(&amp;chopstick[n]); pthread_mutex_lock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d is eating.&apos;, n); sleep(3); pthread_mutex_unlock(&amp;chopstick[n]); pthread_mutex_unlock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d Finished eating &apos;, n); return NULL; } int main() { int i, k; void *message; for (i = 0; i<5; i++) { k="pthread_mutex_init(&amp;chopstick[i]," null); if (k !="0)" printf('failed to initialize the mutex
'); exit(1); } for (i="0;" i< 5; null, func, (void *)&i); printf('error in thread creation.
'); &message); join thread.
'); printf('mutex destroyed.
'); return 0; < pre> <p> <strong>Output:</strong> </p> <pre> Philosopher 0 is thinking. Philosopher 1 is thinking. Philosopher 2 is thinking. Philosopher 3 is thinking. Philosopher 4 is thinking. Philosopher 0 is eating. Philosopher 1 is eating. Philosopher 2 is eating. Philosopher 3 is eating. Philosopher 4 is eating. Philosopher 0 Finished eating Philosopher 1 Finished eating Philosopher 2 Finished eating Philosopher 3 Finished eating Philosopher 4 Finished eating </pre> <p> <strong>Explanation:</strong> </p> <p> <strong> <em>Chopsticks</em> </strong> can be represented by a semaphore. Since there are <strong> <em>chopsticks</em> </strong> on the table and no philosopher has chosen one, all of the chopsticks&apos; components are first initialized to <strong> <em>1</em> </strong> . Now that <strong> <em>chopstick[i]</em> </strong> has been chosen as the first <strong> <em>chopstick. chopstick[i]</em> </strong> and <strong> <em>chopstick[(i+1)%5]</em> </strong> are subject to the first wait operation. These <strong> <em>chopsticks&apos; wait operation</em> </strong> indicates that the philosopher has picked them up. The eating process begins once the philosopher selects his <strong> <em>chopstick</em> </strong> . The signal operation is now carried out on the <strong> <em>chopsticks [i]</em> </strong> and <strong> <em>[(i+1)%5]</em> </strong> once the philosopher has finished eating. The philosopher then turns back to sleep.</p> <p>To determine whether the <strong> <em>subthread</em> </strong> has joined the main thread or not, we used the <strong> <em>pthread_join function</em> </strong> . Similarly, we have checked whether the <strong> <em>mutex</em> </strong> lock has been initialized using the <strong> <em>pthread_mutex_init</em> </strong> method.</p> <p>To initialize and verify whether the new thread was created or not, we utilized the <strong> <em>pthread_create function</em> </strong> . Similar to this, we destroyed the <strong> <em>mutex lock</em> </strong> using the <strong> <em>pthread_mutex_destroy</em> </strong> function.</p> <h2>The Producer-Consumer Problem:</h2> <p>A common issue with multithreading process synchronization is the <strong> <em>producer-consumer problem</em> </strong> . Two processes are present in it: the first is the <strong> <em>producer&apos;s process</em> </strong> , and the second is the <strong> <em>consumer&apos;s process</em> </strong> . Furthermore, it is assumed that both operations are occurring concurrently in parallel. Additionally, they are a cooperative process, which implies that they are sharing something with one another. It is important that when the buffer is <strong> <em>full</em> </strong> , the producer cannot add data. When the buffer is empty, the consumer cannot extract data from the buffer because the common buffer size between the producer and the consumer is <strong> <em>fixed</em> </strong> . The issue is stated in this way. Hence, to implement the Producer-Consumer problem and solve it, we shall employ the idea of parallel programming.</p> <p> <strong>Example:</strong> </p> <pre> #include #include int mutex = 1, full = 0, empty = 3, x = 0; int main() { int n; void producer(); void consumer(); int wait(int); int signal(int); printf(&apos;
1.producer
2.consumer
3.for exit&apos;); while (1) { printf(&apos;
 Please enter your choice:&apos;); scanf(&apos;%d&apos;, &amp;n); switch (n) { case 1: if ((mutex == 1) &amp;&amp; (empty != 0)) producer(); else printf(&apos;Oops!! the buffer is full!!&apos;); break; case 2: if ((mutex == 1) &amp;&amp; (full != 0)) consumer(); else printf(&apos;Oops!! the buffer is empty!!&apos;); break; case 3: exit(0); break; } } return 0; } int wait(int s) { return (--s); } int signal(int s) { return (++s); } void producer() { mutex = wait(mutex); full = signal(full); empty = wait(empty); x++; printf(&apos;
Item produced by the Producer %d&apos;, x); mutex = signal(mutex); } void consumer() { mutex = wait(mutex); full = wait(full); empty = signal(empty); printf(&apos;
Item consumed by the Consumer %d&apos;, x); x--; mutex = signal(mutex); } </pre> <p> <strong>Output:</strong> </p> <pre> 1. producer 2. consumer 3. for exit Please enter your choice: </pre> <p> <strong>Explanation:</strong> </p> <p>We perform two tasks. The functions <strong> <em>consumer()</em> </strong> and <strong> <em>producer()</em> </strong> indicate the status and operation of the <strong> <em>consumer</em> </strong> and <strong> <em>producer</em> </strong> . The <strong> <em>producer() method</em> </strong> will create the <strong> <em>mutex lock</em> </strong> and determine whether the buffer is <strong> <em>full</em> </strong> when it is called. When the buffer is full, nothing will be produced. If not, it will <strong> <em>create</em> </strong> , and then, after the <strong> <em>production</em> </strong> , it will put itself to sleep to unlock the <strong> <em>mutex lock</em> </strong> . Like the <strong> <em>producer</em> </strong> , the consumer first creates the <strong> <em>mutex lock</em> </strong> , checks the <strong> <em>buffer</em> </strong> , consumes the <strong> <em>product</em> </strong> , and then releases the lock before going back to sleep.</p> <p>A <strong> <em>counter (x)</em> </strong> will be used during manufacturing and will keep growing until the manufacturer produces the item. However, the consumer will make fewer of the same manufactured <strong> <em>item (x)</em> </strong> .</p> <h2>Conclusion:</h2> <p>The idea of using <strong> <em>two</em> </strong> or <strong> <em>more threads</em> </strong> to execute a program is known as <strong> <em>multithreading</em> </strong> in the C programming language. <strong> <em>Multithreading</em> </strong> allows for the simultaneous execution of several tasks. The simplest executable component of a program is a <strong> <em>thread</em> </strong> . The process is the idea that a task can be completed by breaking it up into several smaller <strong> <em>sub-processes</em> </strong> .</p> <p>The header file <strong> <em>pthread.h</em> </strong> is required in order to implement multithreading in C since it cannot be done directly.</p> <hr></5;></pre></5;></pre></5;>

Wyjaśnienie:

W tym kodzie mamy wspólne dane zmienne i liczba czytelników rc . The wr stan zmienna służy do ograniczania dostępu dla proces pisania , oraz muteks służy zapewnieniu wzajemnego wykluczenia dostępu do udostępnianych danych.

Proces czytania jest reprezentowany przez funkcja czytnika(). . The liczba czytelników (rc) wzrasta przed osiągnięciem zamek muteksowy . To używa pthread_cond_wait() czekać na wr stan zmienna, jeśli jest to pierwszy czytelnik (rc == 1) . W rezultacie autorzy nie będą mogli pisać, dopóki wszyscy czytelnicy nie ukończą pracy.

Proces czytnika sprawdza, czy był to plik ostatni czytelnik (rc == 0) i obniża czytelnika liczyć (rc--) po zapoznaniu się z udostępnionymi danymi. Jeśli było, pthread_cond_signal() sygnalizuje wr stan zmienna umożliwiająca kontynuację oczekujących procesów piszących.

Używając pthread_create() I funkcje pthread_join(). , My nowy I dołączyć wiele wątków czytelników w główna funkcja . Do każdego wątku czytelnika przypisany jest indywidualny identyfikator w celach identyfikacyjnych.

Proces pisania:

 wait(wrt); . . WRITE INTO THE OBJECT . signal(wrt); 

W powyższym przykładzie, tak samo jak proces czytelnika , wykonywana jest operacja zwana operacją oczekiwania „wrt” gdy użytkownik chce uzyskać dostęp do danych lub obiektu. Po tym czasie nowy użytkownik nie będzie mógł uzyskać dostępu do obiektu. Gdy użytkownik zakończy pisanie, wykonywana jest kolejna operacja na sygnale wr .

2. problem z blokowaniem i odblokowaniem:

Pomysł A muteks jest używany w wielowątkowości w C, aby zagwarantować, że nie będzie warunki wyścigu pomiędzy wątki . Kiedy wiele wątków zaczyna przetwarzać te same dane jednocześnie, jest to tzw wyścigi . Jeśli jednak zaistnieją takie okoliczności, musimy to zrobić. Używamy blokada mutexu() I odblokować() funkcje aby zabezpieczyć określoną sekcję kodu dla określonego wątku. Tak, że inny wątek nie może rozpocząć wykonywania tej samej operacji. The „sekcja/region krytyczny” to nazwa nadana temu chronionemu obszarowi kodowemu. Przed użyciem udostępnionych zasobów konfigurujemy wiele w określonym obszarze, a gdy skończymy z nimi korzystać, odblokowujemy je ponownie.

Przeanalizujmy działanie mutexu do blokowania i odblokowywania w wielowątkowości w C:

Przykład:

 #include #include #include pthread_mutex_tmy_mutex = PTHREAD_MUTEX_INITIALIZER; int shared_data = 0; void *thread_function(void *arg) { pthread_mutex_lock(&amp;my_mutex); shared_data++; // Modify the shared data printf(&apos;Thread %ld: Shared data modified. New value: %d
&apos;, (long)arg, shared_data); pthread_mutex_unlock(&amp;my_mutex); return NULL; } int main() { pthread_tthreads[5]; // Assuming 5 threads for (int i = 0; i<5; i++) { if (pthread_create(&threads[i], null, thread_function, (void *)(long)(i + 1)) !="0)" fprintf(stderr, \'error creating thread %d
\', i 1); return 1; } for (int i< 5; (pthread_join(threads[i], null) joining 0; < pre> <p> <strong>Output:</strong> </p> <pre> Thread 1: Shared data modified. New value: 1 Thread 2: Shared data modified. New value: 2 Thread 3: Shared data modified. New value: 3 Thread 4: Shared data modified. New value: 4 Thread 5: Shared data modified. New value: 5 </pre> <p> <strong>Explanation:</strong> </p> <p>In this above example, we explain how we <strong> <em>lock</em> </strong> and <strong> <em>unlock</em> </strong> a certain region of code that shields us from the racing situation. <strong> <em>&apos;pthread_mutex_t&apos;</em> </strong> is used as an <strong> <em>initializer</em> </strong> in the example above. <strong> <em>&apos;pthread_mutex_lock&apos;</em> </strong> is then <strong> <em>written</em> </strong> before the beginning of the code that we want to lock. The coding that we wish to lock is finished after that. After that, the locking of the code is terminated using <strong> <em>&apos;pthread_mutex_unlock&apos;</em> </strong> ; going forward, no code will be in lock mode.</p> <h2>The Dining Philosopher Problem:</h2> <p>One of the classic issues with synchronization is the <strong> <em>dining philosopher issue</em> </strong> . Simple resource allocation for several processes is required but shouldn&apos;t result in a <strong> <em>stalemate</em> </strong> or <strong> <em>hunger</em> </strong> . The <strong> <em>dining philosopher problem</em> </strong> can be viewed as a straightforward representation of a number of processes, each of which is demanding resources. Since each of these processes requires a resource allocation, it is necessary to distribute those resources across all of the processes so that no one process ever gets stuck or stops working.</p> <p>Assume there are five philosophers seated at a <strong> <em>circle-shaped table</em> </strong> . They eat at one point and ponder about something at another. Around the round table, the philosophers are evenly spaced out on the chairs. Additionally, there is a bowl of rice and five chopsticks for each philosopher in the middle of the table. When the philosopher feels she cannot interact with her colleagues who are seated nearby.</p> <p>A philosopher occasionally takes up two chopsticks when she becomes hungry. She chooses two chopsticks from her neighbors-one on her <strong> <em>left</em> </strong> and one on her <strong> <em>right</em> </strong> -that are within easy reach. But the philosopher should never pick up more than one chopstick at once. She will obviously be unable to pick up the chopstick that the neighbor is using.</p> <p> <strong>Example:</strong> </p> <p>Let&apos;s use an example to demonstrate how this is implemented in C.</p> <pre> #include #include #include #include #include pthread_tphilosopher[5]; pthread_mutex_tchopstick[5]; void *func(void *arg) { int n = *(int *)arg; printf(&apos;
Philosopher %d is thinking.&apos;, n); pthread_mutex_lock(&amp;chopstick[n]); pthread_mutex_lock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d is eating.&apos;, n); sleep(3); pthread_mutex_unlock(&amp;chopstick[n]); pthread_mutex_unlock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d Finished eating &apos;, n); return NULL; } int main() { int i, k; void *message; for (i = 0; i<5; i++) { k="pthread_mutex_init(&amp;chopstick[i]," null); if (k !="0)" printf(\'failed to initialize the mutex
\'); exit(1); } for (i="0;" i< 5; null, func, (void *)&i); printf(\'error in thread creation.
\'); &message); join thread.
\'); printf(\'mutex destroyed.
\'); return 0; < pre> <p> <strong>Output:</strong> </p> <pre> Philosopher 0 is thinking. Philosopher 1 is thinking. Philosopher 2 is thinking. Philosopher 3 is thinking. Philosopher 4 is thinking. Philosopher 0 is eating. Philosopher 1 is eating. Philosopher 2 is eating. Philosopher 3 is eating. Philosopher 4 is eating. Philosopher 0 Finished eating Philosopher 1 Finished eating Philosopher 2 Finished eating Philosopher 3 Finished eating Philosopher 4 Finished eating </pre> <p> <strong>Explanation:</strong> </p> <p> <strong> <em>Chopsticks</em> </strong> can be represented by a semaphore. Since there are <strong> <em>chopsticks</em> </strong> on the table and no philosopher has chosen one, all of the chopsticks&apos; components are first initialized to <strong> <em>1</em> </strong> . Now that <strong> <em>chopstick[i]</em> </strong> has been chosen as the first <strong> <em>chopstick. chopstick[i]</em> </strong> and <strong> <em>chopstick[(i+1)%5]</em> </strong> are subject to the first wait operation. These <strong> <em>chopsticks&apos; wait operation</em> </strong> indicates that the philosopher has picked them up. The eating process begins once the philosopher selects his <strong> <em>chopstick</em> </strong> . The signal operation is now carried out on the <strong> <em>chopsticks [i]</em> </strong> and <strong> <em>[(i+1)%5]</em> </strong> once the philosopher has finished eating. The philosopher then turns back to sleep.</p> <p>To determine whether the <strong> <em>subthread</em> </strong> has joined the main thread or not, we used the <strong> <em>pthread_join function</em> </strong> . Similarly, we have checked whether the <strong> <em>mutex</em> </strong> lock has been initialized using the <strong> <em>pthread_mutex_init</em> </strong> method.</p> <p>To initialize and verify whether the new thread was created or not, we utilized the <strong> <em>pthread_create function</em> </strong> . Similar to this, we destroyed the <strong> <em>mutex lock</em> </strong> using the <strong> <em>pthread_mutex_destroy</em> </strong> function.</p> <h2>The Producer-Consumer Problem:</h2> <p>A common issue with multithreading process synchronization is the <strong> <em>producer-consumer problem</em> </strong> . Two processes are present in it: the first is the <strong> <em>producer&apos;s process</em> </strong> , and the second is the <strong> <em>consumer&apos;s process</em> </strong> . Furthermore, it is assumed that both operations are occurring concurrently in parallel. Additionally, they are a cooperative process, which implies that they are sharing something with one another. It is important that when the buffer is <strong> <em>full</em> </strong> , the producer cannot add data. When the buffer is empty, the consumer cannot extract data from the buffer because the common buffer size between the producer and the consumer is <strong> <em>fixed</em> </strong> . The issue is stated in this way. Hence, to implement the Producer-Consumer problem and solve it, we shall employ the idea of parallel programming.</p> <p> <strong>Example:</strong> </p> <pre> #include #include int mutex = 1, full = 0, empty = 3, x = 0; int main() { int n; void producer(); void consumer(); int wait(int); int signal(int); printf(&apos;
1.producer
2.consumer
3.for exit&apos;); while (1) { printf(&apos;
 Please enter your choice:&apos;); scanf(&apos;%d&apos;, &amp;n); switch (n) { case 1: if ((mutex == 1) &amp;&amp; (empty != 0)) producer(); else printf(&apos;Oops!! the buffer is full!!&apos;); break; case 2: if ((mutex == 1) &amp;&amp; (full != 0)) consumer(); else printf(&apos;Oops!! the buffer is empty!!&apos;); break; case 3: exit(0); break; } } return 0; } int wait(int s) { return (--s); } int signal(int s) { return (++s); } void producer() { mutex = wait(mutex); full = signal(full); empty = wait(empty); x++; printf(&apos;
Item produced by the Producer %d&apos;, x); mutex = signal(mutex); } void consumer() { mutex = wait(mutex); full = wait(full); empty = signal(empty); printf(&apos;
Item consumed by the Consumer %d&apos;, x); x--; mutex = signal(mutex); } </pre> <p> <strong>Output:</strong> </p> <pre> 1. producer 2. consumer 3. for exit Please enter your choice: </pre> <p> <strong>Explanation:</strong> </p> <p>We perform two tasks. The functions <strong> <em>consumer()</em> </strong> and <strong> <em>producer()</em> </strong> indicate the status and operation of the <strong> <em>consumer</em> </strong> and <strong> <em>producer</em> </strong> . The <strong> <em>producer() method</em> </strong> will create the <strong> <em>mutex lock</em> </strong> and determine whether the buffer is <strong> <em>full</em> </strong> when it is called. When the buffer is full, nothing will be produced. If not, it will <strong> <em>create</em> </strong> , and then, after the <strong> <em>production</em> </strong> , it will put itself to sleep to unlock the <strong> <em>mutex lock</em> </strong> . Like the <strong> <em>producer</em> </strong> , the consumer first creates the <strong> <em>mutex lock</em> </strong> , checks the <strong> <em>buffer</em> </strong> , consumes the <strong> <em>product</em> </strong> , and then releases the lock before going back to sleep.</p> <p>A <strong> <em>counter (x)</em> </strong> will be used during manufacturing and will keep growing until the manufacturer produces the item. However, the consumer will make fewer of the same manufactured <strong> <em>item (x)</em> </strong> .</p> <h2>Conclusion:</h2> <p>The idea of using <strong> <em>two</em> </strong> or <strong> <em>more threads</em> </strong> to execute a program is known as <strong> <em>multithreading</em> </strong> in the C programming language. <strong> <em>Multithreading</em> </strong> allows for the simultaneous execution of several tasks. The simplest executable component of a program is a <strong> <em>thread</em> </strong> . The process is the idea that a task can be completed by breaking it up into several smaller <strong> <em>sub-processes</em> </strong> .</p> <p>The header file <strong> <em>pthread.h</em> </strong> is required in order to implement multithreading in C since it cannot be done directly.</p> <hr></5;></pre></5;>

Wyjaśnienie:

typ powrotu w Javie

W powyższym przykładzie wyjaśniamy, jak my zamek I odblokować pewien region kodu, który chroni nas przed sytuacją wyścigową. „pthread_mutex_t” jest używany jako inicjator w powyższym przykładzie. „pthread_mutex_lock” jest wtedy pisemny przed początkiem kodu, który chcemy zablokować. Następnie kodowanie, które chcemy zablokować, jest zakończone. Następnie blokowanie kodu zostaje zakończone za pomocą „pthread_mutex_unlock” ; w przyszłości żaden kod nie będzie w trybie blokady.

Problem filozofa jedzącego:

Jednym z klasycznych problemów związanych z synchronizacją jest kwestia filozofa jadalni . Wymagana jest prosta alokacja zasobów dla kilku procesów, ale nie powinna ona skutkować a pat Lub głód . The problem filozofa jadalni można postrzegać jako prostą reprezentację szeregu procesów, z których każdy wymaga zasobów. Ponieważ każdy z tych procesów wymaga alokacji zasobów, konieczne jest rozdzielenie tych zasobów pomiędzy wszystkie procesy, tak aby żaden proces nigdy nie utknął lub nie przestał działać.

Załóżmy, że w jednym miejscu siedzi pięciu filozofów stół w kształcie koła . W jednym momencie jedzą, a w innym zastanawiają się nad czymś. Wokół okrągłego stołu filozofowie są równomiernie rozmieszczeni na krzesłach. Dodatkowo na środku stołu znajduje się miska ryżu i pięć pałeczek dla każdego filozofa. Kiedy filozof czuje, że nie może wchodzić w interakcje z kolegami, którzy siedzą w pobliżu.

Filozofka czasami bierze do ręki dwie pałeczki, kiedy odczuwa głód. Wybiera dwie pałeczki od sąsiadów – jedną na sobie lewy i jeden na niej Prawidłowy -które są w zasięgu ręki. Ale filozof nigdy nie powinien brać do ręki więcej niż jednej pałeczki na raz. Najwyraźniej nie będzie mogła podnieść pałeczki, której używa sąsiadka.

Przykład:

Użyjmy przykładu, aby zademonstrować, jak jest to zaimplementowane w C.

 #include #include #include #include #include pthread_tphilosopher[5]; pthread_mutex_tchopstick[5]; void *func(void *arg) { int n = *(int *)arg; printf(&apos;
Philosopher %d is thinking.&apos;, n); pthread_mutex_lock(&amp;chopstick[n]); pthread_mutex_lock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d is eating.&apos;, n); sleep(3); pthread_mutex_unlock(&amp;chopstick[n]); pthread_mutex_unlock(&amp;chopstick[(n + 1) % 5]); printf(&apos;
Philosopher %d Finished eating &apos;, n); return NULL; } int main() { int i, k; void *message; for (i = 0; i<5; i++) { k="pthread_mutex_init(&amp;chopstick[i]," null); if (k !="0)" printf(\'failed to initialize the mutex
\'); exit(1); } for (i="0;" i< 5; null, func, (void *)&i); printf(\'error in thread creation.
\'); &message); join thread.
\'); printf(\'mutex destroyed.
\'); return 0; < pre> <p> <strong>Output:</strong> </p> <pre> Philosopher 0 is thinking. Philosopher 1 is thinking. Philosopher 2 is thinking. Philosopher 3 is thinking. Philosopher 4 is thinking. Philosopher 0 is eating. Philosopher 1 is eating. Philosopher 2 is eating. Philosopher 3 is eating. Philosopher 4 is eating. Philosopher 0 Finished eating Philosopher 1 Finished eating Philosopher 2 Finished eating Philosopher 3 Finished eating Philosopher 4 Finished eating </pre> <p> <strong>Explanation:</strong> </p> <p> <strong> <em>Chopsticks</em> </strong> can be represented by a semaphore. Since there are <strong> <em>chopsticks</em> </strong> on the table and no philosopher has chosen one, all of the chopsticks&apos; components are first initialized to <strong> <em>1</em> </strong> . Now that <strong> <em>chopstick[i]</em> </strong> has been chosen as the first <strong> <em>chopstick. chopstick[i]</em> </strong> and <strong> <em>chopstick[(i+1)%5]</em> </strong> are subject to the first wait operation. These <strong> <em>chopsticks&apos; wait operation</em> </strong> indicates that the philosopher has picked them up. The eating process begins once the philosopher selects his <strong> <em>chopstick</em> </strong> . The signal operation is now carried out on the <strong> <em>chopsticks [i]</em> </strong> and <strong> <em>[(i+1)%5]</em> </strong> once the philosopher has finished eating. The philosopher then turns back to sleep.</p> <p>To determine whether the <strong> <em>subthread</em> </strong> has joined the main thread or not, we used the <strong> <em>pthread_join function</em> </strong> . Similarly, we have checked whether the <strong> <em>mutex</em> </strong> lock has been initialized using the <strong> <em>pthread_mutex_init</em> </strong> method.</p> <p>To initialize and verify whether the new thread was created or not, we utilized the <strong> <em>pthread_create function</em> </strong> . Similar to this, we destroyed the <strong> <em>mutex lock</em> </strong> using the <strong> <em>pthread_mutex_destroy</em> </strong> function.</p> <h2>The Producer-Consumer Problem:</h2> <p>A common issue with multithreading process synchronization is the <strong> <em>producer-consumer problem</em> </strong> . Two processes are present in it: the first is the <strong> <em>producer&apos;s process</em> </strong> , and the second is the <strong> <em>consumer&apos;s process</em> </strong> . Furthermore, it is assumed that both operations are occurring concurrently in parallel. Additionally, they are a cooperative process, which implies that they are sharing something with one another. It is important that when the buffer is <strong> <em>full</em> </strong> , the producer cannot add data. When the buffer is empty, the consumer cannot extract data from the buffer because the common buffer size between the producer and the consumer is <strong> <em>fixed</em> </strong> . The issue is stated in this way. Hence, to implement the Producer-Consumer problem and solve it, we shall employ the idea of parallel programming.</p> <p> <strong>Example:</strong> </p> <pre> #include #include int mutex = 1, full = 0, empty = 3, x = 0; int main() { int n; void producer(); void consumer(); int wait(int); int signal(int); printf(&apos;
1.producer
2.consumer
3.for exit&apos;); while (1) { printf(&apos;
 Please enter your choice:&apos;); scanf(&apos;%d&apos;, &amp;n); switch (n) { case 1: if ((mutex == 1) &amp;&amp; (empty != 0)) producer(); else printf(&apos;Oops!! the buffer is full!!&apos;); break; case 2: if ((mutex == 1) &amp;&amp; (full != 0)) consumer(); else printf(&apos;Oops!! the buffer is empty!!&apos;); break; case 3: exit(0); break; } } return 0; } int wait(int s) { return (--s); } int signal(int s) { return (++s); } void producer() { mutex = wait(mutex); full = signal(full); empty = wait(empty); x++; printf(&apos;
Item produced by the Producer %d&apos;, x); mutex = signal(mutex); } void consumer() { mutex = wait(mutex); full = wait(full); empty = signal(empty); printf(&apos;
Item consumed by the Consumer %d&apos;, x); x--; mutex = signal(mutex); } </pre> <p> <strong>Output:</strong> </p> <pre> 1. producer 2. consumer 3. for exit Please enter your choice: </pre> <p> <strong>Explanation:</strong> </p> <p>We perform two tasks. The functions <strong> <em>consumer()</em> </strong> and <strong> <em>producer()</em> </strong> indicate the status and operation of the <strong> <em>consumer</em> </strong> and <strong> <em>producer</em> </strong> . The <strong> <em>producer() method</em> </strong> will create the <strong> <em>mutex lock</em> </strong> and determine whether the buffer is <strong> <em>full</em> </strong> when it is called. When the buffer is full, nothing will be produced. If not, it will <strong> <em>create</em> </strong> , and then, after the <strong> <em>production</em> </strong> , it will put itself to sleep to unlock the <strong> <em>mutex lock</em> </strong> . Like the <strong> <em>producer</em> </strong> , the consumer first creates the <strong> <em>mutex lock</em> </strong> , checks the <strong> <em>buffer</em> </strong> , consumes the <strong> <em>product</em> </strong> , and then releases the lock before going back to sleep.</p> <p>A <strong> <em>counter (x)</em> </strong> will be used during manufacturing and will keep growing until the manufacturer produces the item. However, the consumer will make fewer of the same manufactured <strong> <em>item (x)</em> </strong> .</p> <h2>Conclusion:</h2> <p>The idea of using <strong> <em>two</em> </strong> or <strong> <em>more threads</em> </strong> to execute a program is known as <strong> <em>multithreading</em> </strong> in the C programming language. <strong> <em>Multithreading</em> </strong> allows for the simultaneous execution of several tasks. The simplest executable component of a program is a <strong> <em>thread</em> </strong> . The process is the idea that a task can be completed by breaking it up into several smaller <strong> <em>sub-processes</em> </strong> .</p> <p>The header file <strong> <em>pthread.h</em> </strong> is required in order to implement multithreading in C since it cannot be done directly.</p> <hr></5;>

Wyjaśnienie:

Pałeczki do jedzenia może być reprezentowany przez semafor. Ponieważ istnieją pałeczki do jedzenia na stole i żaden filozof jej nie wybrał, najpierw inicjalizowane są wszystkie elementy pałeczek 1 . Teraz to pałeczka [i] został wybrany jako pierwszy pałeczki np. do ryżu. pałeczka [i] I pałeczka [(i+1)%5] podlegają pierwszej operacji oczekiwania. Te operacja oczekiwania pałeczek wskazuje, że filozof je podniósł. Proces jedzenia rozpoczyna się w momencie, gdy filozof wybierze swój pałeczki np. do ryżu . Operacja sygnalizacyjna jest teraz wykonywana na pałeczki [i] I [(i+1)%5] gdy filozof skończył jeść. Następnie filozof ponownie zasypia.

Aby ustalić, czy podwątek dołączył do głównego wątku, czy nie, użyliśmy metody funkcja pthread_join . Podobnie sprawdziliśmy, czy muteks blokada została zainicjowana przy użyciu metody pthread_mutex_init metoda.

Aby zainicjować i sprawdzić, czy nowy wątek został utworzony, czy nie, użyliśmy metody funkcja pthread_create . Podobnie zniszczyliśmy zamek muteksowy używając pthread_mutex_destroy funkcjonować.

Problem producent-konsument:

Częstym problemem związanym z synchronizacją procesów wielowątkowych jest problem producent-konsument . Występują w nim dwa procesy: pierwszy to proces producenta , a drugie to proces konsumenta . Ponadto zakłada się, że obie operacje zachodzą jednocześnie i równolegle. Ponadto są to procesy kooperacyjne, co oznacza, że ​​dzielą się czymś między sobą. Ważne jest, aby gdy bufor był pełny , producent nie może dodać danych. Gdy bufor jest pusty, konsument nie może wyodrębnić danych z bufora, ponieważ wspólny rozmiar bufora między producentem a konsumentem jest taki naprawił . Sprawa jest przedstawiona w ten sposób. Stąd, aby zrealizować problem Producent-Konsument i go rozwiązać, posłużymy się ideą programowania równoległego.

Przykład:

 #include #include int mutex = 1, full = 0, empty = 3, x = 0; int main() { int n; void producer(); void consumer(); int wait(int); int signal(int); printf(&apos;
1.producer
2.consumer
3.for exit&apos;); while (1) { printf(&apos;
 Please enter your choice:&apos;); scanf(&apos;%d&apos;, &amp;n); switch (n) { case 1: if ((mutex == 1) &amp;&amp; (empty != 0)) producer(); else printf(&apos;Oops!! the buffer is full!!&apos;); break; case 2: if ((mutex == 1) &amp;&amp; (full != 0)) consumer(); else printf(&apos;Oops!! the buffer is empty!!&apos;); break; case 3: exit(0); break; } } return 0; } int wait(int s) { return (--s); } int signal(int s) { return (++s); } void producer() { mutex = wait(mutex); full = signal(full); empty = wait(empty); x++; printf(&apos;
Item produced by the Producer %d&apos;, x); mutex = signal(mutex); } void consumer() { mutex = wait(mutex); full = wait(full); empty = signal(empty); printf(&apos;
Item consumed by the Consumer %d&apos;, x); x--; mutex = signal(mutex); } 

Wyjście:

co to jest 10 na 100
 1. producer 2. consumer 3. for exit Please enter your choice: 

Wyjaśnienie:

Wykonujemy dwa zadania. Funkcje konsument() I producent() wskazują stan i działanie konsument I producent . The metoda producenta(). stworzy zamek muteksowy i określ, czy bufor jest pełny kiedy się to nazywa. Gdy bufor jest pełny, nic nie zostanie wyprodukowane. Jeśli nie, tak się stanie tworzyć , a następnie, po produkcja , przejdzie w tryb uśpienia, aby odblokować zamek muteksowy . Jak producent , konsument najpierw tworzy zamek muteksowy , sprawdza bufor , zużywa produkt , a następnie zwalnia blokadę przed ponownym pójściem w tryb uśpienia.

A licznik (x) będzie używany podczas produkcji i będzie rósł, dopóki producent nie wyprodukuje przedmiotu. Jednak konsument wyprodukuje mniej tego samego produktu pozycja (x) .

Wniosek:

Pomysł wykorzystania dwa Lub więcej wątków wykonanie programu jest tzw wielowątkowość w języku programowania C. Wielowątkowość pozwala na jednoczesną realizację kilku zadań. Najprostszym wykonywalnym komponentem programu jest plik nitka . Proces polega na tym, że zadanie można wykonać, dzieląc je na kilka mniejszych podprocesy .

Plik nagłówkowy pwątek.h jest wymagany do wdrożenia wielowątkowości w C, ponieważ nie można tego zrobić bezpośrednio.