Hitta felet (shared memory, mutex, thread)

Permalänk

Hitta felet (shared memory, mutex, thread)

Hej, har gjort ett program som är till för att lära mig själv trådar, shared memory och mutual exclusion. Tyvärr så finns det en bugg eller två jag inte kan hitta i min kod (förutom att jag förmodligen programmerat det hela som en nolla från början, men det hör inte hit).

Hade tänkt bygga upp det hela med hjälp av två trådar:

Tråd 1
Mutex lock
Skriver till shared memory buffer
om buffer full -> mutex unlock
vänta på tråd 2

Tråd 2
Mutex lock
Läs från shared memory och skriv ut
om hela buffern är läst -> mutex unlock
vänta på tråd 1

Det hela verkar funka som förväntat tills man kommer fram till den andra tråden, där man ska vänta på att tråd 1 ska ta över verksamheten igen(programmet fastnar i en oändlig loop(har förvisso satt en oändlig loop där den fastnar, men mutex är öppen och tråd1 bör ta över igen ganska omgående???)).

/* Compile with: * cc Task6.c -o Task6 -pthread */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/stat.h> #include <pthread.h> #define SHMSIZE 128 //Shared memory specific part char* addr = 0; int i,j,k; int var1, var2; struct shmid_ds* shm_buf; int shmid; struct post* shmp; //mutex pthread_mutex_t mutex; pthread_mutex_t mutex2; pthread_cond_t w; struct post { int character[10]; }; void* thr_a (void* arg){ /* Thread A */ if(pthread_mutex_trylock(&mutex)!=0){ printf("Error or failed to lock thread"); }else{ printf("\nthr_a locked\n"); } j=0; while (var1++ < 100) { /* write to shmem */ shmp->character[j] = var1; printf("wrote something to buffer\n"); if (j == 9){ // buffer is full if(pthread_mutex_unlock(&mutex)!=0){ printf("Error or failed to open thread"); }else{ printf("\nThr_a open\n"); while(1); } } j++; } shmdt(addr); //detatches the memory located at "addr" from the adress space of the calling program. shmctl(shmid, IPC_RMID/*Marks the segment to be destroyed*/, shm_buf); //performs an operation specified by the 2nd argument (IPC_RMID) pthread_exit(0); } void* thr_b (void* arg){ /* Thread B */ if(pthread_mutex_trylock(&mutex2)!=0){ printf("Error or failed to lock thread"); }else{ printf("\nthr_b locked\n"); } k=0; while (var2 < 100) { /* read from shmem */ var2 = shmp->character[k]; printf("%d\n", var2); if (k == 9){ // buffer is empty if (pthread_mutex_unlock(&mutex2)!=0){ printf("Error or failed to open thread"); }else{ printf("\nthr_b open\n"); while(1); } } k++; } shmdt(addr); //detatches the memory located at "addr" from the adress space of the calling program. shmctl(shmid, IPC_RMID/*Marks the segment to be destroyed*/, shm_buf); //performs an operation specified by the 2nd argument (IPC_RMID) pthread_exit(0); } void main() { //mutex pthread_mutex_init(&mutex,NULL); pthread_mutex_init(&mutex2,NULL); //shared memory shmid = shmget(IPC_PRIVATE, SHMSIZE, IPC_CREAT | SHM_R | SHM_W ); // returns a identifier for the allocated memory segment. shmp = (struct post*) shmat(shmid, addr, 0); // returns the address of the shared memory segment(of the shmid). // Thread stuff pthread_t tid_a; pthread_t tid_b; pthread_create(&tid_a, NULL, thr_a, NULL); // Create thread 1 pthread_create(&tid_b, NULL, thr_b, NULL); // Create thread 2 pthread_join(tid_a,NULL); pthread_join(tid_b,NULL); }

Permalänk
Medlem

Dina whileloopar ser lite knasiga ut. Istället för att ha while (x <100) bör du ha for (int j = 0; j < 9; j++) {} eftersom den bara skriver/läser 9 gånger.

Tråd B loopar oändligt eftersom den lästa datan aldrig kommer vara över 100 ("while (var2 < 100)"), tråd A skriver ju bara upp till 9.

(sedan behöver du inte ha så många globala variabler som bara används i trådA respektive trådB)

Edit: Oj! Såg att du använder två olika mutex's för de olika trådarna, det ska du inte göra.

Permalänk
Medlem

Några saker:

1) Du utför ingen "mutual exclusion". En mutex är till för att trådarna ska kunna låsa åtkomsten till viss kod eller data. Detta görs genom att de trådar som vill åt datan låser mutexen, utför vad de ska göra, och sedan låser upp den. Medans mutexen är låst blockeras andra trådar som försöker låsa den, fram tills dess att mutexen låses upp av den första tråden.

Det du gör, dock, är att ha två mutexar. När första tråden har låst sin mutex, och andra tråden försöker låsa sin, så kommer andra tråden att lyckas även om första tråden fortfarande har sin låst, eftersom de använder olika mutexar. Detta innebär att det inte blir någon "mutual exclusion".

2) Även denna punkt har med "mutual exclusion" att göra. Du använder ptread_mutex_trylock. Denna kommer att returnera direkt även om mutexen redan var låst. Meningen med denna är att en tråd ska kunna försöka låsa mutexen, se om det lyckades, och beroende på utfallet utföra olika saker. Låstes mutexen har tråden nu ensamrätt till det som skyddas av mutexen, och den kan göra vad den vill där. Låstes mutexen inte utför tråden något annat istället, eftersom den skyddade datan/koden för stunden används av en annan tråd.

Du vill troligtvis använda pthread_mutex_lock istället. Ifall mutexen redan är låst blockerar denna funktion tills dess att mutexen låsts upp.

3) Du låser respektive mutex (vilket borde vara en enda) exakt en gång. När en tråd har kört färdigt för stunden, alltså efter de 9 iterationerna, så låser den upp sin mutex. Nästa gång tråden kör så låser den dock inte sin mutex, så från och med nu kommer det inte att vara någon "mutual exclusion" (och pthread_mutex_unlock kommer att misslyckas).

4) Eftersom du monterar det delade minnet en gång, i huvudtråden, så vill du bara avmontera och avallokera det en gång, även detta i huvudtråden.

5) Och till sist. Du nämner anledningen (som jag kan se) till varför tråd 1 inte upptar körningen igen, nämligen "har förvisso satt en oändlig loop där den fastnar". Båda trådarna har en sådan loop, och båda kommer alltså att stanna kvar i sin loop tills dess att programmet avslutas utifrån.

Det är möjligt att det finns fler saker, men dessa är vad jag har sett.

Edit: Det finns även risk för en race condition. Vilken av trådarna som körs först är odefinierat, så chansen finns att tråd 2 börjar.

Visa signatur

Vill du ha svar? Citera mig gärna.

Permalänk
Citat:

Ursprungligen inskrivet av DasCunt
Dina whileloopar ser lite knasiga ut. Istället för att ha while (x <100) bör du ha for (int j = 0; j < 9; j++) {} eftersom den bara skriver/läser 9 gånger.

Tråd B loopar oändligt eftersom den lästa datan aldrig kommer vara över 100 ("while (var2 < 100)"), tråd A skriver ju bara upp till 9.

(sedan behöver du inte ha så många globala variabler som bara används i trådA respektive trådB)

Edit: Oj! Såg att du använder två olika mutex's för de olika trådarna, det ska du inte göra.

Den skriver/läser inte bara 9 gånger, utan är tänkt att återkomma för att skiva in 11-20 andra gången man besöker tråden osv.

Outputen är tänkt att bli 1 till 100, varje gång man går in i thread A så skriver den 10 tal till buffern, väntar på thread B, går in i thread B, skriver ut de 10 olika talen för att sedan återvända till thread A och ta nästa 10 tal osv.

Hur kommer det sig att tråd b fastnar men inte tråd a? I båda fallen har jag en while(1) efter att mutex har blivit upplåst.

om din edit: Har ändrat till att enbart ha en mutex. (var ganska säker på att det skulle vara enbart en också, men efter massa mixtrande så bestämde jag mig för att prova 2, det är den koden ni råkade få se )

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Förvirradperson
Den skriver/läser inte bara 9 gånger, utan är tänkt att återkomma för att skiva in 11-20 andra gången man besöker tråden osv.

Outputen är tänkt att bli 1 till 100, varje gång man går in i thread A så skriver den 10 tal till buffern, väntar på thread B, går in i thread B, skriver ut de 10 olika talen för att sedan återvända till thread A och ta nästa 10 tal osv.

Hur kommer det sig att tråd b fastnar men inte tråd a? I båda fallen har jag en while(1) efter att mutex har blivit upplåst.

om din edit: Har ändrat till att enbart ha en mutex. (var ganska säker på att det skulle vara enbart en också, men efter massa mixtrande så bestämde jag mig för att prova 2, det är den koden ni råkade få se )

Trådar fungerar inte som funktioner man anropar. En tråd lever tills den har gjort sitt, sen får man skapa en ny tråd en andra och tredje gång osv. Vad du kan göra är att ha ett event som du signalerar när tråden ska arbeta (som den väntar på, vet inte till vilken platform du skriver men in win32 finns waitforsingleobject tex).

Isåfall kan koden se ut ungefär såhär:

loop:
* lås mutex
* jobba ...
* lås upp mutex
* vänta på event -> loop

Om du bara låser upp mutex:et i vissa fall i Tråd A ("om buffer full -> mutex unlock") så kommer det bli ett deadlock när du anropar den (skapar en ny tråd) en andra gång.

Vet inte hur pedagogisk jag är just nu, tror inte jag helt har förstått dig.

Permalänk
Citat:

Ursprungligen inskrivet av lajnold
text

Perfekt, har redan börjat se över min kod och visst fanns där både pinsamma fel och mindre slarvfel. Tack för din hjälp! Återkommer eventuellt lite senare.