logo

MULTITREADING IN C

Invoering:

In C, de term 'multithreading' beschrijft het gebruik van talrijke draden gelijktijdig. Elke draad doet a andere taak . Vanwege het gelijktijdige karakter van multithreading kunnen veel taken tegelijk worden uitgevoerd. Aanvullend, multithreading vermindert de Het bronnengebruik van de CPU . Er zijn twee categorieën van multitasken: procesmatig En op draad gebaseerd . Wanneer iets wordt beschreven als multithreading, betekent dit dat er minstens twee of misschien meer threads tegelijk in hetzelfde proces worden uitgevoerd. We moeten eerst begrijpen wat een thread en een proces zijn om multithreading in C te begrijpen. Laten we naar deze onderwerpen kijken om een ​​beter begrip te krijgen.

zweven naar touw

Wat zijn processen en threads?

A draad is de fundamenteel gebouw blok van de uitvoering van welk proces dan ook. Een programma bestaat uit verschillende processen, en elk proces bestaat uit threads, die veel fundamentelere eenheden zijn. Daarom kan de thread worden beschouwd als de fundamentele bouwsteen van een proces of als de eenvoudigere eenheid die gezamenlijk het CPU-gebruik bepaalt.

De volgende items zijn opgenomen in een thread:

Onderwerp-ID:

Het is een bijzondere draad-ID die wordt gegenereerd op het moment van draadvorming en wordt bewaard voor de duur van die specifieke draad.

Programmateller:

Het is een waarde die de hardwarebelastingen .

Een geregistreerde set:

Het is een verzameling van gemeenschappelijke registers .

Een stapel:

Het is daar een overblijfsel van specifieke draad .

Als twee threads tegelijkertijd in hetzelfde proces werken, delen ze bovendien code , gegevenssecties en andere besturingssysteembronnen zoals file opent En signalen . Een zwaar proces, een soort conventioneel proces, kan één draad beheersen. Een multi-thread van controle heeft echter de capaciteit om meerdere taken tegelijkertijd te openen en uit te voeren. Door het gebruik van threads wordt het systeem aanzienlijk effectiever en daarom zijn ze nuttig.

Het onderscheid tussen enkel En multithreading in C wordt uitgelegd. In de eerste plaats is het een single-thread proces . Als gevolg hiervan wordt het hele blok, inclusief de code, gegevens, enz. - wordt beschouwd als één proces, en dat proces heeft slechts één thread. Het betekent dat deze techniek slechts één taak tegelijk zal voltooien. Maar er is een multithreading-proces die zich hiertegen verzet. Er zijn activiteiten zoals code, stapel, gegevens , En bestanden ook, maar ze worden uitgevoerd door verschillende threads, die elk hun eigen stapel en registers hebben. Aangezien in deze situatie meerdere taken tegelijk kunnen worden voltooid, staat het proces bekend als een multithreading-proces .

Draad is er in twee varianten:

Onderwerp op gebruikersniveau:

Het bevindt zich op gebruikersniveau, zoals de naam zou impliceren. De kernel krijgt geen toegang tot zijn gegevens.

Thread op kernelniveau

Het soort thread verwijst naar de relatie van de thread met de kernel en het besturingssysteem van het systeem.

Proces- De reeks stappen die worden genomen om een ​​programma uit te voeren, kan worden aangeduid als de proces . Een programma wordt niet onmiddellijk uitgevoerd wanneer het wordt uitgevoerd. Het is opgesplitst in een paar basisstappen die op een georganiseerde manier achtereenvolgens worden uitgevoerd om uiteindelijk tot de uitvoering van een proces te leiden.

jasmijn davis als kind

Een proces dat in kleinere stappen is opgesplitst, wordt een 'kloon- of kindproces', terwijl het oorspronkelijke proces wordt aangeduid als de ‘ouderproces’ . In het geheugen gebruikt elk proces een bepaalde hoeveelheid ruimte die niet wordt gedeeld met andere processen.

Een procedure doorloopt een aantal fasen voordat deze wordt uitgevoerd.

NIEUW-

In deze situatie is er sprake van een nieuw proces gegenereerd .

KLAAR-

Wanneer een proces wordt voorbereid en wacht op de toewijzing van een processor, bevindt het zich in deze status.

RENNEN-

Wanneer het proces actief is, is dit de status.

AAN HET WACHTEN-

Wanneer een proces zich in deze toestand bevindt, is er iets aan het wachten gebeuren.

BEËINDIGD-

Het is de staat waarin de procedure wordt uitgevoerd.

Waarom is C multithreaded?

Multithreading in het C-idee kan via parallellisme worden benut om een functionaliteit van de applicatie . Neem het geval waarin u meerdere tabbladen geopend heeft in een browservenster. Vervolgens werkt elk tabblad gelijktijdig en kan dit een Draad . Stel dat we gebruiken Microsoft Excel , één draad volstaat tekstopmaak , en één draad zal dat doen ingang verwerken . Daarom maakt de multithreading-functie van C het eenvoudig om meerdere taken tegelijk uit te voeren. Het aanmaken van een thread gaat aanzienlijk sneller. De contextoverdracht tussen threads gebeurt sneller. Bovendien kan de communicatie tussen threads sneller plaatsvinden en is het beëindigen van threads eenvoudig.

Hoe C-programma's schrijven voor multithreading?

Hoewel multithreading-applicaties niet in de C-taal zijn ingebouwd, is dit mogelijk, afhankelijk van het besturingssysteem. De threads.h standaardbibliotheek wordt gebruikt om het multithreading-idee te implementeren C . Er is momenteel echter geen compiler die dit kan doen. We moeten platformspecifieke implementaties gebruiken, zoals de 'POSIX' threads-bibliotheek, met behulp van het headerbestand pthread.h , als we multithreading willen gebruiken in C. 'Pthreads' is een andere naam hiervoor. A POSIX draad kan op de volgende manieren worden gemaakt:

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

In dit geval, Pthread_create maakt een nieuwe thread aan om de thread uitvoerbaar te maken. Hiermee kunt u multithreading in C zo vaak als u wilt in uw code implementeren. De parameters en hun eerdere beschrijvingen worden hier vermeld.

draad:

Het is een enkelvoudige identificatie dat de subproces retourneert .

attr:

Wanneer we draadattributen willen instellen, gebruiken we dit ondoorzichtig attribuut .

start_routine:

Wanneer start_routine wordt gegenereerd, voert de thread een routine uit.

wat is 10 van de 100

arg:

De parameter waarmee de start_routine ontvangt. NUL wordt gebruikt als er geen argumenten worden gegeven.

Bepaalde C-multithreading-voorbeelden

Hier zijn enkele voorbeelden van multithreading-problemen in C.

1. De lezers-schrijverkwestie

Een veelvoorkomend probleem met het besturingssysteem bij processynchronisatie is het lezer/schrijver probleem . Stel dat we een database hebben die Lezers En Schrijvers , twee verschillende gebruikerscategorieën, hebben toegang. Lezers zijn de enigen die dat kunnen lezen de database, terwijl Schrijvers zijn de enigen die de database kunnen lezen en ook kunnen bijwerken. Laten we gebruiken IRCTC als eenvoudig voorbeeld. Als we de status van een specifiek willen controleren trein nummer Voer eenvoudigweg het treinnummer in het systeem in om de relevante treininformatie te bekijken. Alleen de informatie die op de website aanwezig is, wordt hier getoond. De leesoperator is deze. Als we echter een ticket willen reserveren, moeten we het ticketboekingsformulier invullen met details zoals onze naam, leeftijd, enzovoort. We zullen hier dus een schrijfbewerking uitvoeren. Er zullen enkele aanpassingen plaatsvinden in de IRCTC-database .

Het probleem is dat verschillende mensen tegelijkertijd proberen toegang te krijgen tot de IRCTC-database . Ze zijn misschien een auteur of een lezer . Het probleem doet zich voor als een lezer de database al gebruikt en een schrijver er tegelijkertijd toegang toe heeft om aan dezelfde gegevens te werken. Een ander probleem doet zich voor wanneer een schrijver een database gebruikt en een lezer toegang krijgt tot dezelfde informatie als de database. Ten derde is er een probleem wanneer de ene schrijver de database bijwerkt, terwijl een andere schrijver gegevens in dezelfde database probeert bij te werken. Het vierde scenario doet zich voor wanneer twee lezers hetzelfde materiaal proberen op te halen. Al deze problemen doen zich voor als de lezer en schrijver dezelfde databasegegevens gebruiken.

Semafoor is een methode die wordt gebruikt om dit probleem op te lossen. Laten we eens kijken naar een illustratie van hoe u dit probleem kunt gebruiken.

Lezersproces:

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

Uitleg:

In deze code hebben we de gedeelde variabele gegevens en de aantal lezers rc . De wrt voorwaarde variabele wordt gebruikt om de toegang voor de schrijver proces , en de mutex wordt gebruikt om wederzijdse uitsluiting van toegang tot de gedeelde gegevens te garanderen.

Het leesproces wordt weergegeven door de reader() functie . De aantal lezers (rc) wordt verhoogd voordat het wordt bereikt mutex-slot . Het gebruikt pthread_cond_wait() te wachten op de wrt voorwaarde variabele als het de eerste lezer (rc == 1) . Als gevolg hiervan zullen schrijvers pas kunnen schrijven als alle lezers klaar zijn.

Het leesproces controleert of dit het laatste lezer (rc == 0) en verlaagt de lezer tellen (rc--) na het lezen van de gedeelde gegevens. Als het was, pthread_cond_signal() signaleert de wrt voorwaarde variabele om wachtende schrijfprocessen te laten doorgaan.

De ... gebruiken pthread_create() En pthread_join() functies , Wij nieuw En meedoen meerdere lezerthreads in de hoofdfunctie . Voor identificatiedoeleinden wordt aan elke lezerthread een individuele ID toegewezen.

Schrijver proces:

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

In het bovenstaande voorbeeld, hetzelfde als de lezer proces , wordt een bewerking uitgevoerd die bekend staat als de wachtbewerking 'wrt' wanneer een gebruiker toegang wil tot de gegevens of het object. Daarna heeft de nieuwe gebruiker geen toegang meer tot het object. En zodra de gebruiker klaar is met schrijven, wordt er nog een signaalbewerking uitgevoerd tov .

2. Probleem met vergrendelen en ontgrendelen:

Het idee van een mutex wordt gebruikt bij multithreading in C om te garanderen dat er geen race conditie tussen de draden . Wanneer meerdere threads dezelfde gegevens in één keer beginnen te verwerken, staat deze omstandigheid bekend als racen . Als deze omstandigheden zich echter voordoen, moeten we dat wel doen. Wij gebruiken de mutex's slot() En unlock() functies om een ​​bepaald codegedeelte voor een specifieke thread te beveiligen. Zodanig dat een andere thread niet dezelfde bewerking kan uitvoeren. De 'kritieke sectie/regio' is de naam die aan dit beschermde codegebied is gegeven. Voordat we de gedeelde bronnen gebruiken, zetten we veel op in een bepaald gebied, en als we klaar zijn met het gebruik ervan, ontgrendelen we ze opnieuw.

Laten we de werking van de mutex voor vergrendelen en ontgrendelen in multithreading in C onderzoeken:

Voorbeeld:

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

Uitleg:

willekeurig getal gen java

In dit bovenstaande voorbeeld leggen we uit hoe we dat doen slot En ontgrendelen een bepaald codegebied dat ons beschermt tegen de racesituatie. 'pthread_mutex_t' wordt gebruikt als een initialisator in het bovenstaande voorbeeld. 'pthread_mutex_lock' is dan geschreven vóór het begin van de code die we willen vergrendelen. Daarna is de codering die we willen vergrendelen voltooid. Daarna wordt de vergrendeling van de code beëindigd met behulp van 'pthread_mutex_unlock' ; in de toekomst zal er geen code in de vergrendelingsmodus staan.

Het probleem van de eetfilosoof:

Een van de klassieke problemen met synchronisatie is het kwestie van eetfilosoof . Eenvoudige toewijzing van middelen voor verschillende processen is vereist, maar mag niet resulteren in een patstelling of honger . De probleem van de eetfilosoof kan worden gezien als een eenvoudige weergave van een aantal processen, die elk hulpbronnen vereisen. Omdat elk van deze processen een toewijzing van middelen vereist, is het noodzakelijk om deze middelen over alle processen te verdelen, zodat geen enkel proces ooit vastloopt of stopt met werken.

Stel dat er vijf filosofen zitten aan een tafel cirkelvormige tafel . De ene keer eten ze en de andere keer nadenken ze over iets. Rond de ronde tafel zitten de filosofen gelijkmatig verdeeld op de stoelen. Daarnaast staat er voor elke filosoof een kom rijst en vijf eetstokjes in het midden van de tafel. Wanneer de filosoof het gevoel heeft dat ze geen interactie kan hebben met haar collega's die vlakbij zitten.

Een filosoof pakt af en toe twee eetstokjes als ze honger krijgt. Ze kiest twee eetstokjes van haar buren, één voor haar links en één op haar rechts -die binnen handbereik zijn. Maar de filosoof mag nooit meer dan één eetstokje tegelijk oppakken. Het is duidelijk dat ze het eetstokje dat de buurvrouw gebruikt niet kan oppakken.

Voorbeeld:

Laten we een voorbeeld gebruiken om te demonstreren hoe dit in C is geïmplementeerd.

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

Uitleg:

Eetstokjes kan worden weergegeven door een semafoor. Aangezien er zijn eetstokjes op tafel ligt en geen enkele filosoof er één heeft gekozen, worden eerst alle componenten van de eetstokjes geïnitialiseerd 1 . Dat eetstokje[ik] is als eerste gekozen eetstokje. eetstokje[ik] En eetstokje[(i+1)%5] zijn onderworpen aan de eerste wachtoperatie. Deze wachtoperatie van eetstokjes geeft aan dat de filosoof ze heeft opgepikt. Het eetproces begint zodra de filosoof de zijne selecteert eetstokje . De signaalbewerking wordt nu uitgevoerd op de eetstokjes [ik] En [(i+1)%5] zodra de filosoof klaar is met eten. De filosoof gaat dan weer slapen.

Om te bepalen of de subthread zich bij de rode draad heeft aangesloten of niet, we hebben de pthread_join-functie . Op dezelfde manier hebben we gecontroleerd of de mutex lock is geïnitialiseerd met behulp van de pthread_mutex_init methode.

Om te initialiseren en te verifiëren of de nieuwe thread al dan niet is aangemaakt, hebben we de pthread_create-functie . Op dezelfde manier vernietigden we de mutex-slot de ... gebruiken pthread_mutex_destroy functie.

Het producent-consumentprobleem:

Een veelvoorkomend probleem bij multithreading-processynchronisatie is het producent-consument probleem . Er zijn twee processen in aanwezig: de eerste is de het proces van de producent , en de tweede is de het proces van de consument . Bovendien wordt aangenomen dat beide bewerkingen gelijktijdig en parallel plaatsvinden. Bovendien zijn ze een coöperatief proces, wat inhoudt dat ze iets met elkaar delen. Het is belangrijk dat wanneer de buffer aanwezig is vol , kan de producent geen gegevens toevoegen. Wanneer de buffer leeg is, kan de consument geen gegevens uit de buffer extraheren, omdat de gemeenschappelijke buffergrootte tussen de producent en de consument dat is vast . Op deze manier wordt de kwestie verwoord. Om het producent-consumentprobleem te implementeren en op te lossen, zullen we daarom het idee van parallelle programmering gebruiken.

Voorbeeld:

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

Uitgang:

Hoe een geheel getal naar een string in Java te converteren
 1. producer 2. consumer 3. for exit Please enter your choice: 

Uitleg:

Wij voeren twee taken uit. De functies klant() En producent() geeft de status en werking aan van de klant En producent . De producer() methode zal de creëren mutex-slot en bepaal of de buffer dat is vol wanneer er gebeld wordt. Als de buffer vol is, wordt er niets geproduceerd. Zo niet, dan zal dat wel gebeuren creëren , en dan, na de productie , zal het zichzelf in slaap brengen om het te ontgrendelen mutex-slot . Zoals de producent , creëert de consument eerst de mutex-slot , controleert de buffer , verbruikt de Product en ontgrendelt vervolgens de vergrendeling voordat hij weer gaat slapen.

A teller (x) zal worden gebruikt tijdens de productie en zal blijven groeien totdat de fabrikant het artikel produceert. De consument zal echter minder van hetzelfde product maken artikel (x) .

Conclusie:

Het idee van het gebruiken twee of meer draadjes het uitvoeren van een programma staat bekend als multithreading in de programmeertaal C. Multithreading maakt de gelijktijdige uitvoering van meerdere taken mogelijk. Het eenvoudigste uitvoerbare onderdeel van een programma is een draad . Het proces is het idee dat een taak kan worden voltooid door deze in meerdere kleinere taken op te delen subprocessen .

Het headerbestand pthread.h is vereist om multithreading in C te implementeren, omdat dit niet rechtstreeks kan worden gedaan.