logo

DAUGIAIGIJIMAS C

Įvadas:

C – terminas 'daugiagija' aprašomas daugelio naudojimas siūlai vienu metu. Kiekviena gija atlieka a skirtinga užduotis . Dėl kelių gijų kūrimo vienu metu pobūdžio galima atlikti daug užduočių vienu metu. Be to, daugiasriegis sumažina CPU išteklių naudojimas . Yra dvi kelių užduočių kategorijos: procesų pagrindu ir siūlų pagrindu . Kai kas nors apibūdinama kaip kelių gijų kūrimas, tai reiškia, kad tame pačiame procese vienu metu veikia bent dvi ar galbūt daugiau gijų. Pirmiausia turime suprasti, kas yra gija ir procesas, kad suprastume kelių gijų kūrimą C. Pažvelkime į šiuos dalykus, kad geriau suprastume.

komandą touch Linux sistemoje

Kas yra procesai ir gijos?

A siūlas yra pamatinis pastatas blokas bet kokio proceso vykdymo. Programą sudaro keli procesai, o kiekvienas procesas yra sudarytas iš gijų, kurios yra daug paprastesni vienetai. Todėl giją galima laikyti pagrindiniu proceso elementu arba paprastesniu vienetu, kuris kartu lemia procesoriaus panaudojimą.

Į giją įtraukti šie elementai:

Gijos ID:

Tai ypatinga gijos ID kuri sukuriama gijos formavimo metu ir išsaugoma tos konkrečios gijos trukmę.

Programų skaitiklis:

Tai vertybė, kurią aparatūros apkrovos .

Registruotas rinkinys:

Tai kolekcija bendrieji registrai .

Krūva:

Tai yra to likutis konkretus siūlas .

Be to, jei tame pačiame procese vienu metu veikia dvi gijos, jos dalijasi kodas , duomenų skyriai , ir kitus operacinės sistemos išteklius, pvz., failą atsidaro ir signalus . Sunkus procesas, įprastas procesas, gali valdyti vieną giją. Tačiau kelių gijų valdymas gali vienu metu atidaryti ir atlikti kelias užduotis. Naudojant siūlus, sistema tampa žymiai efektyvesnė, todėl jos yra naudingos.

Skirtumas tarp vienišas ir daugiasriegis C yra paaiškinta. Visų pirma, tai a vienos gijos procesas . Dėl to visas blokas, įskaitant kodas, duomenys, ir tt – yra laikomas vienu procesu, ir tas procesas turi tik vieną giją. Tai reiškia, kad ši technika vienu metu atliks tik vieną užduotį. Tačiau yra a kelių gijų procesas kuri tam prieštarauja. Yra tokios veiklos kodas, kaminas, duomenys , ir failus taip pat, bet juos atlieka kelios gijos, kurių kiekviena turi savo krūvą ir registrus. Atsižvelgiant į tai, kad šioje situacijoje vienu metu galima atlikti daugybę užduočių, procesas vadinamas a kelių gijų procesas .

Siūlai būna dviejų rūšių:

Tema vartotojo lygiu:

Tai yra vartotojo lygiu, kaip rodo pavadinimas. Branduoliui nesuteikiama prieiga prie jo duomenų.

Siūlas branduolio lygiu

dekoduoti base64 javascript

Gijos rūšis nurodo gijos ryšį su sistemos branduoliu ir operacine sistema.

Procesas - Veiksmų, kurių imtasi vykdant programą, serija gali būti vadinama procesas . Programa paleidžiama ne iš karto. Jis suskirstytas į kelis pagrindinius veiksmus, kurie nuosekliai atliekami organizuotu būdu, kad galiausiai būtų įvykdytas procesas.

Procesas, suskaidytas į mažesnius etapus, vadinamas a „klono arba vaiko procesas“, o pradinis procesas vadinamas „tėvų“ procesas . Atmintyje kiekvienas procesas naudoja tam tikrą kiekį vietos, kuri nėra bendrinama su jokiais kitais procesais.

Prieš vykdymą procedūra praeina keletą etapų.

NAUJIENA-

Šioje situacijoje vyksta naujas procesas sukurtas .

PASIRENGTAS-

Kai procesas yra paruoštas ir laukia, kol bus priskirtas procesorius, jis yra šios būsenos.

BĖGANTIS-

Kai procesas aktyvus, tai yra būsena.

LAUKIA-

Kai procesas yra tokioje būsenoje, kažkas yra laukimas nutikti.

NUTRAUKTA-

Tai valstybė, kurioje atliekama procedūra.

Kodėl C yra daugiasriegis?

Daugiagija C idėjoje gali būti panaudotas lygiagretumas, siekiant sustiprinti programos funkcionalumą . Apsvarstykite atvejį, kai naršyklės lange yra atidaryti keli skirtukai. Tada kiekvienas skirtukas veikia vienu metu ir gali būti vadinamas a Siūlas . Darant prielaidą, kad naudojame Microsoft Excel , susitvarkys viena gija teksto formatavimas , ir vienas siūlas bus rankenos įvestis . Todėl C multithreading funkcija leidžia lengvai atlikti kelias užduotis vienu metu. Gijos sukūrimas yra žymiai greitesnis. Konteksto perkėlimas tarp gijų vyksta greičiau. Be to, ryšį tarp gijų galima užmegzti greičiau, o giją užbaigti paprasta.

Kaip parašyti C programas, skirtas kelių sriegių kūrimui?

Nors kelių gijų programos nėra integruotos į C kalbą, tai įmanoma, priklausomai nuo operacinės sistemos. The threads.h standartinė biblioteka naudojamas kelių gijų idėjai įgyvendinti C . Tačiau šiuo metu nėra kompiliatoriaus, kuris galėtų tai padaryti. Turime naudoti konkrečioms platformoms pritaikytus diegimus, tokius kaip 'POSIX' gijų biblioteką, naudodami antraštės failą pthread.h , jei norime naudoti daugiagiją C. 'P gijos' tai kitas pavadinimas. A POSIX giją galima sukurti šiais būdais:

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

Tokiu atveju, Pthread_create sukuria naują giją, kad giją būtų galima vykdyti. Tai leidžia jums įdiegti daugiagiją C tiek kartų, kiek norite savo kode. Čia pateikiami ankstesni parametrai ir jų aprašymai.

gija:

Tai yra vienaskaita identifikacija kad subprocesas grąžina .

java nepakeičiamas sąrašas

atr.:

Kai norime nustatyti gijos atributus, naudojame tai nepermatomas atributas .

start_rutina:

Kada start_rutina sugeneruotas, gija veiks pagal rutiną.

arg:

Parametras, kurį start_rutina gauna. NULL bus naudojamas, jei nebus pateikta jokių argumentų.

Tam tikri C kelių gijų pavyzdžiai

Štai keletas kelių gijų problemų, susijusių su C.

1. Skaitytojo ir rašytojo problema

Dažna operacinės sistemos problema, susijusi su procesų sinchronizavimu, yra skaitytojo/rašytojo problema . Tarkime, kad turime duomenų bazę Skaitytojai ir Rašytojai , gali pasiekti dvi skirtingos vartotojų kategorijos. Skaitytojai yra vieninteliai, kurie gali skaityti duomenų bazėje, tuo tarpu Rašytojai yra vieninteliai, kurie gali skaityti duomenų bazę ir ją atnaujinti. Naudokimės IRCTC kaip paprastas pavyzdys. Jei norime patikrinti konkretaus būseną traukinio numeris , tiesiog įveskite traukinio numerį į sistemą, kad peržiūrėtumėte susijusią traukinio informaciją. Čia rodoma tik ta informacija, kuri yra svetainėje. Skaitymo operatorius yra toks. Tačiau, jei norime rezervuoti bilietą, turime užpildyti bilietų užsakymo formą su tokia informacija kaip mūsų vardas, amžius ir pan. Taigi, čia atliksime rašymo operaciją. Bus atlikti tam tikri pakeitimai IRCTC duomenų bazė .

Problema ta, kad keli žmonės vienu metu bando pasiekti IRCTC duomenų bazė . Jie gali būti a rašytojas arba a skaitytojas . Problema iškyla, jei skaitytojas jau naudojasi duomenų baze, o rašytojas tuo pačiu metu ją pasiekia, kad dirbtų su tais pačiais duomenimis. Kita problema iškyla, kai rašytojas naudojasi duomenų baze, o skaitytojas pasiekia tą pačią informaciją kaip ir duomenų bazė. Trečia, kyla sunkumų, kai vienas rašytojas atnaujina duomenų bazę, o kitas bando atnaujinti duomenis toje pačioje duomenų bazėje. Ketvirtasis scenarijus įvyksta, kai du skaitytojai bando gauti tą pačią medžiagą. Visos šios problemos kyla, jei skaitytojas ir rašytojas naudoja tuos pačius duomenų bazės duomenis.

Semaforas yra metodas, naudojamas šiai problemai išspręsti. Pažvelkime į iliustraciją, kaip naudoti šią problemą.

Skaitymo procesas:

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

Paaiškinimas:

Šiame kode yra bendrinamų kintamųjų duomenų ir skaitytojų skaičius rc . The wrt būklė kintamasis naudojamas apriboti prieigą prie rašytojo procesas , ir mutex yra naudojamas siekiant užtikrinti abipusį pašalinimą norint pasiekti bendrinamus duomenis.

Skaitymo procesą vaizduoja Reader() funkcija . The skaitytojų skaičius (rc) padidinamas prieš pasiekiant mutex užraktas . Tai naudoja pthread_cond_wait() laukti wrt būklė kintamasis, jei jis yra pirmasis skaitytojas (rc == 1) . Dėl to rašytojai negalės rašyti, kol visi skaitytojai nebaigs.

Skaitymo procesas patikrina, ar tai buvo paskutinis skaitytojas (rc == 0) ir nuleidžia skaitytoją skaičius (rc--) perskaitę bendrinamus duomenis. Jei buvo, pthread_cond_signal() signalizuoja wrt būklė kintamasis, leidžiantis tęsti laukiančius rašymo procesus.

Naudojant pthread_create() ir pthread_join() funkcijos , mes naujas ir prisijungti kelios skaitytojo gijos pagrindinė() funkcija . Kiekvienai skaitymo gijai priskiriamas individualus ID identifikavimo tikslais.

Rašymo procesas:

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

Aukščiau pateiktame pavyzdyje toks pat kaip ir skaitymo procesas , atliekama operacija, vadinama laukimo operacija 'wrt' kai vartotojas nori pasiekti duomenis ar objektą. Po to naujas vartotojas negalės pasiekti objekto. O kai vartotojas baigia rašyti, atliekama kita signalo operacija wrt .

2. užrakinimo ir atrakinimo problema:

Idėja apie a mutex yra naudojamas C multithreading, siekiant užtikrinti, kad nebus a lenktynių būklė tarp siūlai . Kai kelios gijos vienu metu pradeda apdoroti tuos pačius duomenis, ši aplinkybė vadinama lenktynes . Tačiau jei tokios aplinkybės egzistuoja, privalome. Mes naudojame mutex užraktas () ir unlock () funkcijas kad apsaugotumėte tam tikrą kodo sekciją konkrečiai gijai. Taip, kad kita gija negali pradėti atlikti tos pačios operacijos. The 'kritinis skyrius/regionas' yra šios saugomos kodo srities pavadinimas. Prieš naudodami bendrinamus išteklius, tam tikroje srityje nustatome daug, o baigę juos naudoti dar kartą atrakiname.

Panagrinėkime, kaip veikia mutex užrakinant ir atrakinant kelių gijų C:

Pavyzdys:

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

Paaiškinimas:

ilgio eilutė

Šiame aukščiau pateiktame pavyzdyje paaiškiname, kaip mes užraktas ir atrakinti tam tikras kodo regionas, kuris apsaugo mus nuo lenktynių situacijos. „pthread_mutex_t“ yra naudojamas kaip iniciatorius aukščiau pateiktame pavyzdyje. „pthread_mutex_lock“ yra tada parašyta prieš kodo, kurį norime užrakinti, pradžią. Kodavimas, kurį norime užrakinti, baigiamas po to. Po to kodo užrakinimas nutraukiamas naudojant „pthread_mutex_unlock“ ; toliau joks kodas nebus užrakinimo režimu.

Valgomojo filosofo problema:

Viena iš klasikinių sinchronizavimo problemų yra valgymo filosofo problema . Reikalingas paprastas išteklių paskirstymas keliems procesams, tačiau tai neturėtų sukelti a aklavietė arba alkis . The valgymo filosofo problema gali būti vertinamas kaip paprastas daugelio procesų, kurių kiekvienas reikalauja išteklių, vaizdas. Kadangi kiekvienam iš šių procesų reikia paskirstyti išteklius, būtina tuos išteklius paskirstyti visiems procesams, kad nė vienas procesas niekada neužstrigtų ar nenustotų veikti.

Tarkime, kad prie a sėdi penki filosofai apskritimo formos stalas . Vienu metu jie valgo, o kitur apie ką nors mąsto. Aplink apvalų stalą filosofai yra tolygiai išdėstyti ant kėdžių. Be to, stalo viduryje yra dubenėlis ryžių ir penkios lazdelės kiekvienam filosofui. Kai filosofė jaučia, kad negali bendrauti su šalia sėdinčiais kolegomis.

Filosofas kartais paima dvi lazdeles, kai tampa alkanas. Ji išsirenka dvi lazdeles iš kaimynų – vieną ant jos paliko ir vienas ant jos teisingai - kurie yra lengvai pasiekiami. Tačiau filosofas niekada neturėtų iš karto paimti daugiau nei vienos lazdelės. Akivaizdu, kad ji negalės paimti kaimyno naudojamo pagaliuko.

Pavyzdys:

Panaudokime pavyzdį, kad parodytume, kaip tai įgyvendinama 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;>

Paaiškinimas:

Lazdelės gali būti pavaizduotas semaforu. Kadangi yra lazdelės ant stalo ir joks filosofas jo nepasirinko, visi lazdelių komponentai pirmiausia inicijuojami 1 . Dabar kai pagaliukas [i] buvo pasirinktas kaip pirmasis pagaliukas. pagaliukas [i] ir lazdelė [(i+1)%5] jiems taikoma pirmoji laukimo operacija. Šie lazdelių laukimo operacija rodo, kad filosofas juos pasiėmė. Valgymo procesas prasideda tada, kai filosofas pasirenka savo pagaliukas . Signalo operacija dabar atliekama lazdelės [i] ir [(i+1)%5] kai filosofas baigia valgyti. Tada filosofas vėl užmiega.

Norint nustatyti, ar pogija prisijungė prie pagrindinės gijos ar ne, mes naudojome pthread_join funkcija . Panašiai patikrinome, ar mutex užraktas buvo inicijuotas naudojant pthread_mutex_init metodas.

Norėdami inicijuoti ir patikrinti, ar nauja gija buvo sukurta, ar ne, panaudojome pthread_create funkcija . Panašiai mes sunaikinome mutex užraktas naudojant pthread_mutex_destroy funkcija.

Gamintojo ir vartotojo problema:

Dažna kelių gijų procesų sinchronizavimo problema yra gamintojo ir vartotojo problema . Jame vyksta du procesai: pirmasis yra gamintojo procesas , o antrasis yra vartotojo procesas . Be to, daroma prielaida, kad abi operacijos vyksta vienu metu lygiagrečiai. Be to, tai yra bendradarbiavimo procesas, o tai reiškia, kad jie kažkuo dalijasi vienas su kitu. Svarbu, kad kai buferis yra pilnas , gamintojas negali pridėti duomenų. Kai buferis tuščias, vartotojas negali gauti duomenų iš buferio, nes bendras buferio dydis tarp gamintojo ir vartotojo yra fiksuotas . Problema išdėstyta taip. Todėl gamintojo-vartotojo problemai įgyvendinti ir ją išspręsti pasitelksime paralelinio programavimo idėją.

git statusas

Pavyzdys:

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

Išvestis:

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

Paaiškinimas:

Atliekame dvi užduotis. Funkcijos vartotojas () ir gamintojas () nurodyti būseną ir veikimą vartotojas ir gamintojas . The gamintojas() metodas sukurs mutex užraktas ir nustatyti, ar buferis yra pilnas kai jis vadinamas. Kai buferis bus pilnas, nieko nebus gaminama. Jei ne, tai bus sukurti , o tada, po gamyba , jis užmigs, kad atrakintų mutex užraktas . Kaip gamintojas , vartotojas pirmiausia sukuria mutex užraktas , patikrina buferis , suvartoja produktas , o tada atleidžia užraktą prieš einant miegoti.

A skaitiklis (x) bus naudojami gamybos metu ir augs tol, kol gamintojas pagamins prekę. Tačiau vartotojas pagamins mažiau tokių pačių gaminių elementas (x) .

Išvada:

Naudojimo idėja du arba daugiau gijų programos vykdymas yra žinomas kaip daugiasriegis C programavimo kalba. Daugiagija leidžia vienu metu atlikti kelias užduotis. Paprasčiausias vykdomasis programos komponentas yra a siūlas . Procesas yra idėja, kad užduotį galima atlikti suskaidžius ją į keletą mažesnių subprocesų .

Antraštės failas pthread.h reikalingas norint įdiegti daugiagiją C, nes to negalima padaryti tiesiogiai.