logo

MULTITHREADING ÎN C

Introducere:

În C, termenul „multithreading” descrie utilizarea numeroaselor fire concomitent. Fiecare fir face a sarcină diferită . Datorită naturii concomitente a multithreading-ului, multe sarcini pot fi efectuate simultan. În plus, multithreading reduce Utilizarea resurselor CPU . Există două categorii de multitasking: bazat pe proces și bazate pe fire . Când ceva este descris ca multithreading, înseamnă că cel puțin două sau poate mai multe fire rulează în același proces simultan. Mai întâi trebuie să înțelegem ce sunt un fir și un proces pentru a înțelege multithreading în C. Să ne uităm la aceste subiecte pentru a obține o înțelegere mai bună.

piton de filtrare

Ce sunt procesele și firele?

A fir este clădire fundamentală bloc a executării oricărui proces. Un program este format din mai multe procese, iar fiecare proces este alcătuit din fire de execuție, care sunt unități mult mai de bază. Prin urmare, firul de execuție poate fi considerat elementul fundamental al unui proces sau unitatea mai simplă care determină împreună utilizarea CPU.

Următoarele articole sunt incluse într-un fir:

ID subiect:

Este un special ID fir care este generată în momentul formării firului și reținută pe durata acelui fir specific.

Contor de programe:

Este o valoare pe care încărcături hardware .

Un set înregistrat:

Este o colecție de registre comune .

O stivă:

Este o rămășiță din asta fir specific .

În plus, dacă două fire funcționează simultan în același proces, ele partajează cod , secțiuni de date și alte resurse ale sistemului de operare, cum ar fi fișierul se deschide și semnale . Un proces greu, un tip de proces convențional, poate controla un fir. Cu toate acestea, un multi-thread de control are capacitatea de a deschide și de a efectua mai multe sarcini simultan. Sistemul devine considerabil mai eficient ca urmare a utilizării firelor, motiv pentru care sunt utile.

Distincția dintre singur și multithreading în C este explicat. În primul rând, este o proces cu un singur fir . Ca urmare, întregul bloc-inclusiv cod, date, etc.-este privit ca un singur proces, iar acel proces are doar un fir. Aceasta înseamnă că această tehnică va îndeplini doar o sarcină la un moment dat. Dar există o proces multithreading care stă în opoziție cu aceasta. Există activități de genul cod, stivă, date , și fișiere de asemenea, dar acestea sunt realizate de mai multe fire de execuție, fiecare dintre ele având propriul stivă și registre. Având în vedere că numeroase sarcini pot fi îndeplinite simultan în această situație, procesul este cunoscut sub numele de a proces multithreading .

Firul vine în două soiuri:

Subiect la nivel de utilizator:

Este la nivel de utilizator, așa cum ar sugera și numele. Nucleul nu are acces la datele sale.

Thread la nivelul nucleului

Tipul de fir se referă la relația firului de execuție cu nucleul și sistemul de operare al sistemului.

Proces- Seria de pași luați pentru realizarea unui program poate fi denumită proces . Un program nu este executat imediat când este rulat. Este împărțit în câțiva pași de bază care sunt desfășurați secvenţial într-o manieră organizată pentru a duce în cele din urmă la execuția unui proces.

Un proces care a fost împărțit în pași mai mici este denumit a „proces clon sau copil”, în timp ce procesul original este denumit procesul „părinte”. . În memorie, fiecare proces folosește o anumită cantitate de spațiu care nu este partajată cu niciun alt proces.

vizualizator java

O procedură parcurge câteva etape înainte de execuție.

NOU-

În această situație, un nou proces este generate .

GATA-

Când un proces este pregătit și așteaptă ca un procesor să fie alocat, acesta se află în această stare.

ALERGARE-

Când procesul este activ, acesta este starea.

AŞTEPTARE-

Când un proces este în această stare, ceva este aşteptare a se intampla.

TERMINAT-

Este starea în care se desfășoară procedura.

De ce este C multithreaded?

Multithreading în ideea C poate fi valorificată prin paralelism pentru a spori an funcționalitatea aplicației . Luați în considerare cazul în care aveți mai multe file deschise într-o fereastră de browser. Apoi, fiecare filă funcționează concomitent și poate fi denumită a Fir . Presupunând că folosim Microsoft Excel , un fir se va descurca formatarea textului , și un fir va manevrează intrarea . Prin urmare, caracteristica multithreading a lui C face simplă realizarea mai multor sarcini simultan. Crearea unui fir este considerabil mai rapidă. Transferul de context între fire se întâmplă mai rapid. În plus, comunicarea între fire se poate face mai rapid, iar terminarea firului este simplă.

Cum se scriu programe C pentru multithreading?

Deși aplicațiile multithreading nu sunt încorporate în limbajul C, este posibil în funcție de sistemul de operare. The fire.h bibliotecă standard este folosit pentru a implementa ideea de multithreading în C . Cu toate acestea, în prezent nu există niciun compilator care să poată face acest lucru. Trebuie să folosim implementări specifice platformei, cum ar fi „POSIX” bibliotecă de fire de execuție, folosind fișierul antet pthread.h , dacă vrem să folosim multithreading în C. „Pthreads” este un alt nume pentru asta. A POSIX firul poate fi creat în următoarele moduri:

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

În acest caz, Pthread_create creează un fir nou pentru a face firul executabil. Vă permite să implementați multithreading în C de câte ori doriți în cod. Parametrii și descrierile lor de mai devreme sunt enumerate aici.

fir:

Este un identificare singulară că cel returnează subprocesul .

attr:

Când vrem să setăm atributele firului, folosim acest lucru atribut opac .

start_rutine:

Când start_rutine este generat, firul de execuție va rula o rutină.

arg:

Parametrul pe care start_rutine primeste. NUL va fi folosit dacă nu sunt date argumente.

do while loop în java

Anumite exemple C multithreading

Iată câteva exemple de probleme de multithreading în C.

1. Problema cititor-scriitor

O problemă comună a sistemului de operare cu sincronizarea procesului este problema cititorului/scriitorului . Să presupunem că avem o bază de date care Cititorii și Scriitori , două categorii de utilizatori diferite, pot accesa. Cititorii sunt singurii care pot citit baza de date, în timp ce Scriitori sunt singurii care pot citi baza de date și o pot actualiza. Să folosim IRCTC ca exemplu simplu. Dacă dorim să verificăm starea unui anume numărul trenului , introduceți pur și simplu numărul trenului în sistem pentru a vedea informațiile relevante despre tren. Doar informațiile care sunt prezente pe site sunt afișate aici. Operatorul de citire este acesta. Totuși, dacă dorim să rezervăm un bilet, trebuie să completăm formularul de rezervare a biletului cu detalii precum numele nostru, vârsta și așa mai departe. Deci, vom efectua o operație de scriere aici. Vor fi făcute unele ajustări la baza de date IRCTC .

Problema este că mai multe persoane încearcă simultan să acceseze baza de date IRCTC . Ar putea fi o scriitor sau a cititor . Problema apare dacă un cititor utilizează deja baza de date și un scriitor o accesează simultan pentru a lucra la aceleași date. O altă problemă apare atunci când un scriitor utilizează o bază de date, iar un cititor accesează aceleași informații ca și baza de date. În al treilea rând, există o dificultate atunci când un scriitor actualizează baza de date în timp ce altul încearcă să actualizeze datele din aceeași bază de date. Al patrulea scenariu apare atunci când doi cititori încearcă să recupereze același material. Toate aceste probleme apar dacă cititorul și scriitorul folosesc aceleași date de bază de date.

Semaforul este o metodă folosită pentru a rezolva această problemă. Să ne uităm la o ilustrare a modului de utilizare a acestei probleme.

Procesul cititorului:

 #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;>

Explicaţie:

În acest cod, avem datele variabile partajate și număr de cititori rc . The stare grea variabila este folosită pentru a limita accesul pentru procesul de scriitor , si mutex este folosit pentru a garanta excluderea reciprocă pentru accesarea datelor partajate.

Procesul cititorului este reprezentat de funcția reader(). . The număr de cititori (rc) este crescut înainte de a atinge blocare mutex . Folosește pthread_cond_wait() a aștepta pe stare grea variabilă dacă este primul cititor (rc == 1) . Drept urmare, scriitorii vor fi împiedicați să scrie până când toți cititorii vor termina.

Procesul de citire verifică dacă a fost ultimul cititor (rc == 0) și coboară cititorul numără (rc--) după citirea datelor partajate. Daca era, pthread_cond_signal() semnalează stare grea variabilă pentru a permite proceselor de scriere în așteptare să continue.

Folosind pthread_create() și funcțiile pthread_join(). , noi nou și a te alatura mai multe fire de citire în functie principala . Un ID individual este atribuit fiecărui fir de citire în scopuri de identificare.

Procesul de scriitor:

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

În exemplul de mai sus, la fel ca și procesul cititorului , se efectuează o operațiune cunoscută sub numele de operațiune de așteptare 'wrt' atunci când un utilizator dorește să acceseze datele sau obiectul. După aceea, noul utilizator nu va mai putea accesa obiectul. Și odată ce utilizatorul a terminat de scris, se efectuează o altă operație de semnal wrt .

2. problemă de blocare și deblocare:

Ideea de a mutex este utilizat în multithreading în C pentru a garanta că nu va exista un starea de cursă între fire . Atunci când mai multe fire de execuție încep să proceseze aceleași date simultan, această circumstanță este cunoscută ca curse . Totuși, dacă aceste circumstanțe există, trebuie. Noi folosim blocarea lui mutex() și funcțiile unlock(). pentru a securiza o anumită secțiune de cod pentru un anumit thread. Astfel încât, un alt thread nu poate începe să efectueze aceeași operațiune. The „secțiune/regiune critică” este numele dat acestei zone de cod protejate. Înainte de a folosi resursele partajate, am configurat multe într-o anumită zonă și, odată ce le-am terminat de folosit, le deblocăm încă o dată.

Să examinăm funcționarea mutex-ului pentru blocarea și deblocarea în multithreading în C:

Exemplu:

 #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;>

Explicaţie:

vlc descărca videoclipuri youtube

În acest exemplu de mai sus, explicăm cum Lacăt și debloca o anumită regiune de cod care ne ferește de situația de curse. „pthread_mutex_t” este folosit ca un initializator în exemplul de mai sus. „pthread_mutex_lock” este atunci scris înainte de începutul codului pe care dorim să-l blocăm. Codarea pe care dorim să o blocăm se termină după aceea. După aceea, blocarea codului este încheiată folosind „pthread_mutex_unlock” ; în continuare, niciun cod nu va fi în modul de blocare.

Problema filozofului mesei:

Una dintre problemele clasice legate de sincronizare este chestiunea filosofului mesei . Alocarea simplă a resurselor pentru mai multe procese este necesară, dar nu ar trebui să aibă ca rezultat a impas sau foame . The problema filozofului mesei poate fi privit ca o reprezentare simplă a unui număr de procese, fiecare dintre ele necesită resurse. Deoarece fiecare dintre aceste procese necesită o alocare de resurse, este necesar să se distribuie acele resurse în toate procesele, astfel încât niciun proces să nu se blocheze sau să nu mai funcționeze.

Să presupunem că există cinci filozofi așezați la a masă în formă de cerc . Ei mănâncă la un moment dat și la altul se gândesc la ceva. În jurul mesei rotunde, filozofii sunt așezați uniform pe scaune. În plus, în mijlocul mesei există un castron cu orez și cinci betisoare pentru fiecare filosof. Când filozoful simte că nu poate interacționa cu colegii săi care stau în apropiere.

Un filozof ia ocazional două bețișoare când îi este foame. Ea alege două betisoare de la vecinii ei - una pe ea stânga și unul pe ea dreapta - care sunt la îndemână. Dar filozoful nu ar trebui să ridice niciodată mai mult de o bețișoară deodată. Evident, ea nu va putea ridica betisa pe care o folosește vecinul.

Exemplu:

Să folosim un exemplu pentru a demonstra cum este implementat acest lucru în 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;>

Explicaţie:

betisoare poate fi reprezentat printr-un semafor. Din moment ce există betisoarele pe masă și niciun filosof nu a ales unul, toate componentele bețișoarelor sunt mai întâi inițializate la 1 . Acum că betisoare[i] a fost ales ca primul betisoare. betisoare[i] și betisoare[(i+1)%5] sunt supuse primei operațiuni de așteptare. Aceste operațiunea de așteptare a bețișoarelor indică faptul că filozoful le-a preluat. Procesul de mâncare începe odată ce filozoful îl alege pe al lui betisoare . Operația de semnal este acum efectuată pe betisoare [i] și [(i+1)%5] odată ce filozoful a terminat de mâncat. Filosoful se întoarce apoi la somn.

Pentru a determina dacă subthread sa alăturat firului principal sau nu, am folosit funcția pthread_join . În mod similar, am verificat dacă mutex blocarea a fost inițializată folosind pthread_mutex_init metodă.

Pentru a inițializa și a verifica dacă noul fir a fost creat sau nu, am folosit funcția pthread_create . Similar cu aceasta, am distrus blocare mutex folosind pthread_mutex_destroy funcţie.

Problema producător-consumator:

O problemă comună cu sincronizarea procesului multithreading este problema producator-consumator . Două procese sunt prezente în el: primul este procesul producătorului , iar al doilea este procesul consumatorului . În plus, se presupune că ambele operațiuni au loc concomitent în paralel. În plus, sunt un proces de cooperare, ceea ce înseamnă că împărtășesc ceva unul cu celălalt. Este important ca atunci când tamponul este deplin , producătorul nu poate adăuga date. Când tamponul este gol, consumatorul nu poate extrage date din tampon deoarece dimensiunea comună a tamponului dintre producător și consumator este fix . Problema este formulată în acest fel. Prin urmare, pentru a implementa problema Producător-Consumator și pentru a o rezolva, vom folosi ideea de programare paralelă.

Exemplu:

 #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); } 

Ieșire:

 1. producer 2. consumer 3. for exit Please enter your choice: 

Explicaţie:

șir la număr întreg

Executăm două sarcini. Funcțiile consumator() și producător() indica starea și funcționarea consumator și producător . The metoda producatorului(). va crea blocare mutex și determinați dacă tamponul este deplin când se numește. Când tamponul este plin, nu se va produce nimic. Dacă nu, va fi crea , și apoi, după producție , se va adormi pentru a debloca blocare mutex . Ca producător , consumatorul creează mai întâi blocare mutex , verifică tampon , consumă produs , apoi eliberează blocarea înainte de a reveni la culcare.

A contor (x) va fi folosit în timpul producției și va continua să crească până când producătorul va produce articolul. Cu toate acestea, consumatorul va face mai puține produse din același produs elementul (x) .

Concluzie:

Ideea de a folosi Două sau mai multe fire a executa un program este cunoscut ca multithreading în limbajul de programare C. Multithreading permite executarea simultană a mai multor sarcini. Cea mai simplă componentă executabilă a unui program este a fir . Procesul este ideea că o sarcină poate fi finalizată împărțind-o în câteva mai mici subprocese .

Fișierul antet pthread.h este necesar pentru a implementa multithreading în C, deoarece nu se poate face direct.