View Full Version : [c threads] variabili locali/globali...
ho un programma che crea piu thread...nel codice di uno di questi (per prova) ho scritto:
int x;
x++;
printf("%d\n", x);
nel main creo una variabile p_thread e creo due volte, una di seguito all altra, il thread e la prima volta stampa 1, la seconda 2...wtf? ma la cosa piu bella:
-creo un thread (con quelle 3 righe)
-subito dopo un altro thread che non contempla neanche l esistenza di x
-creo un altro thread dello stesso tipo del primo
-stampo x e mi da un valore negativo immenso (penso min intero)
buh, la morale e che a me serve avere delle variabili dichiarate all interno del codice di un thread e alle quali abbia accesso solo quel thread e non qualsiasi altra fregnaccia...si puo fare? come? grazie in anticipo
Mentre faccio un up espongo meglio il problema in quanto dnarod, a forza di vedere solo codice, si e' dimenticato l'italiano....
Il programma in questione e' un daemon che rimane in esecuzione in un sistema in attesa di richieste da parte di vari clients che concorrenzialmente lo andranno ad interpellare.
Senza andare troppo nel dettaglio, inutile, ad ogni richiesta questo daemon lancia un programma figlio, nella fattispecie un nuovo thread, che si occupera' di questa richiesta in modo che il programma padre possa rimettersi nuovamente, ed immediatamente, in "ascolto" di nuove richieste.
Il nocciolo del problema e' che per assicurare la concorrenzialita' tra i processi di questo sistema non possiamo far uso del classico pthread_join(che consiste nell'aspettare la terminazione del thread 'x') nel daemon. In questo modo la concorrenzialita' tra i processi viene mantenuta e con il corretto uso dei mutex viene anche mantenuta la mutua esclusione nelle sezioni critiche di ogni thread lanciato.
Fin qua sembra che abbiamo capito tutto e che il programma possa funzionare senza problemi, PERO' durante l'esecuzione abbiamo notato che alcune variabili, dopo un paio di threads lanciati, risultavano "sporche" e qui si riconduce il post di dnarod:
Abbiamo aggiunto nel codice di un thread per prova una variabile intera x, l'abbiamo incrementata e successivamente stampata:
int x;
x++;
printf("%d\n", x);
A questo punto siamo andati a testare il programma lanciando il daemon ed inviando l'opportuna richiesta per far lanciare il thread con quel pezzo di codice.
Alla prima chiamata verra' printato "1". Alla seconda chiamata dello stesso thread verra' stampato "2".
Ad una prima e disattenta analisi questo puo' anche sembrare "giusto", pero' l'intero x dichiarato e creato nel primo thread DEVE(a rigor di logica ed a meno che non ci sfugge qualcosa) essere per forza diverso dall'x dichiarato e creato nel secondo thread e SOPRATTUTTO non puo' essere condiviso e TANTOMENO incrementato dal thread successivo.
Ragionando sulla questione abbiamo pensato(in maniera del tutto ignorante) che sbagliamo a creare i threads ed entrambi sono lo stesso thread. Siamo quindi andati a controllare, thread per thread il pthread_self(che ritorna l'ID del thread) ed ognugno era diverso. Questo significa che ogni thread creato era diverso da quello precedente.
A questo punto com'e' possibile che il secondo, terzo, quarto, ecc ecc... e successivi threads vadano ad intereagire con quella variabile OGNI VOLTA LOCALMENTE DICHIARATA?
Facendo altre prove abbiamo creato un altro thread(chiamamolo Y) che non fa nulla alla variabile x e nel quale non viene nemmeno dichiarata, ed abbiamo testato il programma in questa maniera:
1) thread_x ---- "1"
2) thread_x ---- "2"
3) thread_x ---- "3"
4) thread_Y ---- "ciao sono Y e non ho niente a che fare con la variabile x"
5) thread_x ---- "-8245891"
6) thread_x ---- "-8245890"
Tutto cio' come diavolo e' possibile? Sinceramente non sappiamo piu' cosa pensare e a questo punto crediamo che ci sfugga davvero qualcosa di grosso sui threads. Anche perche' la definizione dell'uso delle variabili nei threads dice che:
Le variabili che vengono dichiarate localmente all'interno della funzione che implementa il codice di ciascun thread sono private di quello specifico thread, e pertanto non accessibili da parte degli altri thread del processo.
:help: :help: :help:
Si può vedere il codice ?
è enorme, si fa solo confusione...metto le parti significative...
-------------------------
dal main:
[...]
pthread_t T;
[...]
while(1) {
pthread_create(&T, NULL, ilnostrothread, NULL);
}
[...]
dal codice del thread:
void* ilnostrothread() {
[...]
int x;
x++;
printf("%d\n", x);
[...]
pthread_exit(NULL);
}
-----------------------------
se viene fatta la stessa richiesta al server piu volte (si creano piu thread) la prima stampa 1, la seconda 2, ecc...le prove sono fatte a mano, quindi i thread muoiono sicuramente e non si incrociano mai...poi magari la cosa ha anche un suo senso logico, ma quel che interessa a me è altro, solo che non so come ottenerlo e non trovo informazioni da altre parti; spero di aver reso l idea, altrimenti mi sa pvt...
x è locale a quell'istanza del thread...ogni thread ha la sua x che viene creata al momento della creazione del thread e distrutta al momento che la funzione del thread termina la sua esecuzione. In teoria gli altri thread potrebbero però accedere a x conoscendone l'indirizzo.
Quindi se x cambia il suo stato senza che venga acceduta dal thread che la crea è possibile che si tratti di un buffer overflow...in pratica gli altri thread cercando di operare su PROPRI dati vanno erroneamente a sovrascrivere il contenuto di x ;)
Si, l'avevamo pensato anche noi però a questo punto come si può risolvere?
Cioè, che senso ha fare threads se poi le variabili PRIVATE di un thread vengono sovrascritte "per sbaglio" da un altro thread che sta scrivendo le sue private?
Non è che questo "per sbaglio" è dovuto a come chiamiamo/usiamo i threads? Non esiste una dichiarazione privata(ahhh Java come ti amo) per queste variabili?
Grazie in anticipo per l'attenzione e le risposte :)
Il modo per risolvere è trovare a cosa è dovuto il buffer overflow ;)
Non certo rendere più "privata" la variabile...
Il valore in cui viene modificato è sempre lo stesso ?
il fatto è che qui funziona cosi:
1)creo il thread
2)il trhead fa quello che deve fare e di sicuro muore (perche lo creiamo a mano con un menu maffo controllato da shell)
3)solo dopo creiamo il prossimo (con un buon secondo di ritardo almeno)...
han tempo a morire 100 threads in quell arco di tempo (alla fine all interno del codice fara si e no una decina di operazioni, cioe nulla), cioe dovrebbe essere impossibile che un thread possa sporcare un altro, anche se è l unica idea che è venuta in mente anche a me...
Mi viene un dubbio...ma questa x la inizializzi ogni volta ? Come fai ad assegnare il valore iniziale a x ?
Viene inizializaata ad ogni chiamata di quel thread. Non viene settato un valore iniziale, int x; lo inizializza a 0.
Questo la prima volta, poi dalla seconda in poi prende l'ultimo valore di x...
int x;
non inizializza assolutamente a zero, se lo fa è dipende dal compilatore...
La variabile deve essere inizializzata a zero ;)
Si, diciamo che mi sono spiegato male, lo so che viene inizializzata random nel range dell'int, però in questo caso abbiamo fatto una comunissima prova ed al primo passaggio i++ printa "1", quindi si, dipende dal compilatore ma questo poco ci interessa.
Se, ad esempio, la inizializzassimo OGNI volta a 0, esempio:
void* ilnostrothread() {
[...]
int x = 0;
x++;
printf("%d\n", x);
[...]
pthread_exit(NULL);
}
Ad ogni chiamata di questo thread avremo sempre printato "1". Quindi, senza ragionarci su, potremmo dire che per risolvere il problema iniziale basterebbe inizializzare a 0/NULL tutte le variabili di cui faremo uso all'interno del thread.
Questo si, andrebbe bene nel caso in cui lanciassimo solo un thread dello "stesso tipo"( pthread_create(&T, NULL, ilnostrothread, NULL); ) alla volta aspettando la sua terminazione con il pthread_join nel main oppure con un mutex che tenga lockato il thread in tutta la sua computazione. Così abbiamo già appurato che funziona, però viene a mancare tutta la parte concorrenziale del progetto.
Finalmente sono riuscito a spiegare il problema? Scusami ma spiegarlo è più difficile di quel che sembra.. :)
Come ho già detto la variabile x è allocata per ogni istanza del thread, quindi non puoi "sperare" che alla successiva istanza mantenga il valore dato precedentemente.
Per far sì che lo mantenga dovresti passare l'indirizzo di x alla pthread_create...
Ad esempio:
int x; //allocata solo una volta
....
....
pthread_create(&T, NULL, ilnostrothread, &x);
void* ilnostrothread(void *arg)
{
int *x = (int *)arg;
(*x)++;
printf(("%d\n", *x);
}
Nota che in questo modo funziona bene solo con un solo thread ilnostrothread in esecuzione...se ne volete avere più di uno contemporaneamente allora avrete problemi di concorrenza e quindi dovreste usare una mutex (ad esempio potete passare l'indirizzo di una struttura contenente x e l'istanza della mutex che lavora su x come argomento del thread).
Si, e ti ringrazio molto della risposta e del tempo che dietro ci stai perdendo, grazie davvero.
Però noi non "speriamo" che la variabile sia condivisa, noi vogliamo che le variabili presenti nei vari threads non siano assolutamente condivise, questo perchè ognuno è assestante.
Abbiamo usato questo esempio della x, ma l'abbiamo usato solo per provare che questa "incredibile condivisione" esiste e ci sta mandato a quel paese tutta la logica del progetto...
Ricapitolando...
Il thread viene fatto girare (chiamiamolo thread1), dichiarate una variabile locale ad un thread. e la variabile ha valore, casualmente devo aggiungere, zero...incrementate questa variabile e la stampate. Stampa 1. A questo punto il thread termina e lo spazio occupato dalla variabile x viene liberato.
Il thread viene fatto girare nuovamente (chiamiamolo thread2), dichiarate una variabile locale ad un thread. e la variabile ha valore pseudocasuale. Voi invece vorreste che avesse valore 1. E' giusto ?
Se c'è qualcosa di errato nel ragionamento che faccio cerca di riprendere le mie parole e di modificarle ad hoc.
Se è giusto allora avete sbagliato dal punto di vista logico in quanto l'istanza di x in thread1 non ha niente a che vedere con l'istanza di x in thread2. Sono variabili diverse con tempi di vita disgiunti. L'unico modo per avere 1 in x nella seconda istanza del thread è che abbiate la fortuna che x, quella nuova, vada ad occupare la stessa posizione di memoria della x vecchia e che nel frattempo nessuno abbia scritto in quella locazione di memoria.
tomminno
06-11-2007, 18:02
Si, e ti ringrazio molto della risposta e del tempo che dietro ci stai perdendo, grazie davvero.
Però noi non "speriamo" che la variabile sia condivisa, noi vogliamo che le variabili presenti nei vari threads non siano assolutamente condivise, questo perchè ognuno è assestante.
Abbiamo usato questo esempio della x, ma l'abbiamo usato solo per provare che questa "incredibile condivisione" esiste e ci sta mandato a quel paese tutta la logica del progetto...
Ma cosa ci dovete fare con questa x?
Volete contare quanti thread sono stati eseguiti? Se si deve essere comune a tutti i thread.
Altrimenti per il codice che avere postato x potrà assumere sempre e solo il valore 1.
Per quanto rigurarda l'inizializzazione, può essere che le successive istanze del thread vadano ad occupare le medesime celle di memoria del precedente, con la conseguenza che per caso la x riassume il valore che aveva in precedenza.
Quando viene istanziato un thread che non viene allocato nello stesso punto dei precedenti succede esattamente quello che riportate.
Quella variabile deve essere inizializzata da ogni istanza del thread, ciò significa che, siccome non la volete condividere tra thread, potrà valere sempre e solo 1 alla fine del thread.
Se c'è qualcosa di errato nel ragionamento che faccio cerca di riprendere le mie parole e di modificarle ad hoc.
Il thread viene fatto girare (chiamiamolo thread1), dichiarate una variabile locale ad un thread. e la variabile ha valore, casualmente devo aggiungere, zero...incrementate questa variabile e la stampate. Stampa 1. A questo punto il thread termina e lo spazio occupato dalla variabile x viene liberato.
Il thread viene fatto girare nuovamente (chiamiamolo thread2), dichiarate una variabile locale ad un thread. e la variabile ha valore pseudocasuale. Noi vorremmo che x avesse questo MALEDETTO valore pseudocasuale ed invece ha il MALEDETTO "1"(assegnato dal thread precedente, "thread1") dentro! Thread2 a questo punto farà "x++" e stamperà "2".
Il successivo "thread3" avrà "2", farà "x++" e stamperà "3".
Questo è assolutamente inamissibile! Le variabili sono dichiarate localmente e non devono risultare condivise in questa maniera :cry:
Guarda, oltretutto io e dnarod non credo che siamo 2 completi dementi della programmazione, non ci sono "bugs di storta" nel codice, tipo una globale int x dichiarata o porcate simili...
Credo di aver spiegato bene il problema qui (http://www.hwupgrade.it/forum/showpost.php?p=19501926&postcount=2) e, se sei ancora così gentile da voler continuare ad aiutarci in questo problema, ti invito a rileggerlo. TY
tomminno
06-11-2007, 18:24
I vostri problemi risiedono nel fatto che non inizializzate le variabili, probabilmente perchè siete abituati al Java che lo fa da sè, mentre il C non lo fa.
Come più volte ripetuto se non inizializzate le variabili il loro valore è random, e se per caso il vostro thread è allocato nelle stesse celle del precedente (cosa non certo rara, osservando in debug spesso si vedono le variabili riassumere il loro vecchio valore, magari anche della precedente istanza), allora per puro caso il valore assunto coincide con quello vecchio e quindi vedete come se la variabile fosse condivisa, in realtà è un vostro errore di programmazione decisamente elementare.
Ma cosa ci dovete fare con questa x?
Volete contare quanti thread sono stati eseguiti? Se si deve essere comune a tutti i thread.
No, non ci interessa. Questo int x lo usiamo per dimostrare che esiste una condivisione di variabili tra un thread e l'altro.
Altrimenti per il codice che avere postato x potrà assumere sempre e solo il valore 1.
E' proprio qui la cosa bella, NOI VORREMMO CHE FOSSE COSI', ma nella realtà dei fatti NON E' COSI'
Per quanto rigurarda l'inizializzazione, può essere che le successive istanze del thread vadano ad occupare le medesime celle di memoria del precedente, con la conseguenza che per caso la x riassume il valore che aveva in precedenza.
Possibile, è quello che stiamo pensando noi.. Ma come risolvere? Mettere dei mutex che lockano tutto il thread risolverebbe il problema ma andrebbe a perdersi tutta la concorrenzialità del progetto..
Quando viene istanziato un thread che non viene allocato nello stesso punto dei precedenti succede esattamente quello che riportate.
Si, ma come facciamo ad allocarlo "NON nello stesso punto dei precedenti"?
L'unico modo che ho di verificare che ogni thread che viene creato è diverso da un altro è controllare attraverso la pthread_self(che ritorna l'ID del thread). Abbiamo controllato e ogni thread è diverso da un altro.
Quella variabile deve essere inizializzata da ogni istanza del thread, ciò significa che, siccome non la volete condividere tra thread, potrà valere sempre e solo 1 alla fine del thread.
Giusto, e sono d'accordo con te ma a questo punto il programma non funziona più in maniera concorrenziale. Qua ora, per semplicità stiamo usando una semplicissima variabile intera x. ma in questi threads noi dobbiamo lavorare su delle strutture e su una serie di variabili molto più ampia di un comune intero.
In ogni caso ti faccio una serie di esempi con questo codice nel thread:
void* ilnostrothread() {
[...]
int x = 0;
sleep(2);
x++;
printf("%d\n", x);
[...]
pthread_exit(NULL);
}
SIMULAZIONE:
1) creazione ilnostrothread_1, inizio computazione ilnostrothread_1, int x = 0, sleep, context switch, proseguimento computazione ilnostrothread_1, x++, x = 1, "1", pthread_exit.
2) creazione ilnostrothread_2, inizio computazione ilnostrothread_2, int x = 0, sleep, context switch, proseguimento computazione ilnostrothread_2, x++, x = 1, "1", pthread_exit.
3) creazione ilnostrothread_3, inizio computazione ilnostrothread_3, int x = 0, sleep, context switch, proseguimento computazione ilnostrothread_3, x++, x = 1, "1", pthread_exit.
FUNZIONA!
SIMULAZIONE POSSIBILE:
1) creazione ilnostrothread_1.
2) inizio computazione ilnostrothread_1, int x = 0, sleep, context switch.
3) creazione ilnostrothread_2.
4) inizio computazione ilnostrothread_2, int x = 0, sleep, context switch.
5) creazione ilnostrothread_3.
6) inizio computazione ilnostrothread_3, int x = 0, sleep, context switch.
7) proseguimento computazione ilnostrothread_1, x++, x = 1, "1", pthread_exit.
8) proseguimento computazione ilnostrothread_1, x++, x = 2, "2", pthread_exit.
9) proseguimento computazione ilnostrothread_1, x++, x = 3, "3", pthread_exit.
NON FUNZIONA!
Grazie anche a te per l'aiuto e la voglia di darci una mano :)
I vostri problemi risiedono nel fatto che non inizializzate le variabili, probabilmente perchè siete abituati al Java che lo fa da sè, mentre il C non lo fa.
Come più volte ripetuto se non inizializzate le variabili il loro valore è random, e se per caso il vostro thread è allocato nelle stesse celle del precedente (cosa non certo rara, osservando in debug spesso si vedono le variabili riassumere il loro vecchio valore, magari anche della precedente istanza), allora per puro caso il valore assunto coincide con quello vecchio e quindi vedete come se la variabile fosse condivisa, in realtà è un vostro errore di programmazione decisamente elementare.
Guarda, si hai assolutamente ragione, ma ribadisco che l'esempio con la x è solo un BANALISSIMO esempio dove non l'abbiamo inizializzata per dimostrare l'incremento tra un thread e l'altro. Il fatto che su Unix Solaris 10 il gcc la voglia inizializzare di suo a 0 è un altro paio di mani che non mi interessa approfondire...
Nel nostro programma ogni variabile è inizializzata LOCALMENTE. Quindi siamo nell'esempio "Esempio con 3 threads che arrivano in maniera concorrenziale(conteporeaneamente)" del mio reply appena postato. Come vedi qui la x che sia inizializzata o meno conta poco, qua va a quel paese l'intera concorrenzialità del programma. E il motivo è dell'inspiegabile situazione in cui queste maledette variabili decidono di meschiarsi tra i threads.
Entrambi(io e dnarod) abbiamo ben chiaro cosa succede quando questo codice viene eseguito e quella simulazione è uno dei possibili casi(poi dipende dallo scheduler), ma non abbiamo la minima idea di come risolvere per il semplice fatto che IN TEORIA QUELLE VARIABILI NON SI DOVREBBERO CONDIVIDERE.
Grazie ancora per il supporto.. :)
AnonimoVeneziano
06-11-2007, 18:46
Quello che succede è di semplice interpretazione.
Le variabili locali sono allocate nello stack.
Ogni volta che il thread termina semplicemente la cima dello stack viene spostata in alto in modo da non contare più lo spazio occupato dalle variabili locali del thread (che è molto simile ad una funzione). Non viene sovrascritto nulla. I valori sono ancora lì, solo che lo spostamento della cima dello stack fa in modo che per il programma siano cancellati.
Al momento dell'esecuzione del thread successivo la cima dello stack viene rispostata in basso per fare posto alla nuova variabile "x" del nuovo thread che, non venendo inizializzata, ha ancora il valore di quella del vecchio.
Probabilmente il valore sarebbe stato "veramente casuale" se l'esecuzione dei thread avvenissero veramente in contemporanea. (cioè ad esempio due o più thread vengono creati allo stesso tempo). Con molta probabilità questo non avviene. Questi thread sembrano essere molto rapidi da eseguire e più che essere eseguiti in parallelo vengono eseguiti in maniera seriale (come fossero delle funzioni, perchè un thread finisce prima che quello successivo sia stato creato) . La cosa si dovrebbe far sentire ancor di più su una macchina monoprocessore/monocore.
Ciao
Quello che succede è di semplice interpretazione.
Le variabili locali sono allocate nello stack.
Ogni volta che il thread termina semplicemente la cima dello stack viene spostata in alto in modo da non contare più lo spazio occupato dalle variabili locali del thread (che è molto simile ad una funzione). Non viene sovrascritto nulla. I valori sono ancora lì, solo che lo spostamento della cima dello stack fa in modo che per il programma siano cancellati.
Al momento dell'esecuzione del thread successivo la cima dello stack viene rispostata in basso per fare posto alla nuova variabile "x" del nuovo thread che, non venendo inizializzata, ha ancora il valore di quella del vecchio.
Probabilmente il valore sarebbe stato "veramente casuale" se l'esecuzione dei thread avvenissero veramente in contemporanea. (cioè ad esempio due o più thread vengono creati allo stesso tempo). Con molta probabilità questo non avviene. Questi thread sembrano essere molto rapidi da eseguire e più che essere eseguiti in parallelo vengono eseguiti in maniera seriale (come fossero delle funzioni, perchè un thread finisce prima che quello successivo sia stato creato) . La cosa si dovrebbe far sentire ancor di più su una macchina monoprocessore/monocore.
Ciao
Grazie anche a te per la tua competente risposta. Diciamo che anche questo "caso" l'avevamo analizzato. Sia io che dnarod siamo convinti che un difficilmente avverrà un context switch durante l'esecuzione di questo threads. Il problema è che questo progetto ci viene chiesto di testarlo con più threads simultaneamente(sleep inserite qua e la in modo da stopparne la computazione ed avere più di uno stesso thread contemporaneamente in esecuzione) e quindi questo "bug"(lol non ho idea di come definirlo) ci porta a meschiare queste variabili locali.
Invito anche te a leggere, se hai voglia s'intende, l'esempio che ho postato qui (http://www.hwupgrade.it/forum/showpost.php?p=19507487&postcount=19) quotato come "Esempio con 3 threads che arrivano in maniera concorrenziale(conteporeaneamente)". Perchè questo è quello che abbiamo potuto simulare e toccare con mano testandolo direttamente sulla macchina.
AnonimoVeneziano
06-11-2007, 18:57
No, non ci interessa. Questo int x lo usiamo per dimostrare che esiste una condivisione di variabili tra un thread e l'altro.
E' proprio qui la cosa bella, NOI VORREMMO CHE FOSSE COSI', ma nella realtà dei fatti NON E' COSI'
Possibile, è quello che stiamo pensando noi.. Ma come risolvere? Mettere dei mutex che lockano tutto il thread risolverebbe il problema ma andrebbe a perdersi tutta la concorrenzialità del progetto..
Si, ma come facciamo ad allocarlo "NON nello stesso punto dei precedenti"?
L'unico modo che ho di verificare che ogni thread che viene creato è diverso da un altro è controllare attraverso la pthread_self(che ritorna l'ID del thread). Abbiamo controllato e ogni thread è diverso da un altro.
Giusto, e sono d'accordo con te ma a questo punto il programma non funziona più in maniera concorrenziale. Qua ora, per semplicità stiamo usando una semplicissima variabile intera x. ma in questi threads noi dobbiamo lavorare su delle strutture e su una serie di variabili molto più ampia di un comune intero.
In ogni caso ti faccio una serie di esempi con questo codice nel thread:
void* ilnostrothread() {
[...]
int x = 0;
sleep(2);
x++;
printf("%d\n", x);
[...]
pthread_exit(NULL);
}
Grazie anche a te per l'aiuto e la voglia di darci una mano :)
Sembra quasi come se lo stack venga scalato alla fine di un thread col risultato che il thread spawnato dopo va ad utilizzare il valore di "X" del thread precedente ...
Una cosa tipo :
1) il primo thread inizia , inizializza X a zero e ci somma 1, fa printf X , SWITCH
2) il secondo thread inizia, inizializza la propria X a zero (che sta sopra alla X del primo thread non ancora terminato) , SWITCH
3) il primo thread termina e lo stack viene ripristinato (facendolo crescere in modo da eliminare la variabile X del primo thread, ma in realtà viene eliminata quella del secondo thread che è quella in cima allo stack)
4) il secondo thread somma 1 alla X del primo thread (erroneamente) e la stampa a schermo, SWITCH
5) continua così anche con gli altri thread.
Potrebbe essere un possibile motivo del malfunzionamento .... anche se mi sembra un po' inverosimile.
Non sono un grande esperto di pthreads.
Ciao
I thread per definizione condividono lo spazio di indirizzamento del processo/thread che li crea. Quindi di qui non si scappa. Due thread concorrenti girano nello stesso spazio di indirizzamento, altrimenti non sarebbero concorrenti e la concorrenza dovrebbe essere stabilita tramite meccanismi di comunicazione interprocesso (vedi pipe, fifo, mailslot etc).
Già, è molto inverosimile ma qua siamo alla frutta, è da giorni che siamo fermi. Abbiamo ricompilato e riscritto da 0 codice per testare queste cose ma il risultato è quello..
Conosciamo il problema ma non sappiamo proprio come risolverlo, speriamo che a qualcuno venga una bella ideona, se no... :cry:
In ogni caso grazie per averci provato :)
Sembra quasi come se lo stack venga scalato alla fine di un thread col risultato che il thread spawnato dopo va ad utilizzare il valore di "X" del thread precedente ...
Ma questo è naturale ed avviene anche con le funzioni...se invece di richiamare quella funzione come un thread avrei lo stesso comportamento:
ilnostrothread(NULL);
ilnostrothread(NULL);
Molto probabilmente stamperebbe 1 e 2.
Nel codice che avete postato non c'è alcun malfunzionamento. E' semplicemente lo stato delle cose.
AnonimoVeneziano
06-11-2007, 19:02
I thread per definizione condividono lo spazio di indirizzamento del processo/thread che li crea. Quindi di qui non si scappa. Due thread concorrenti girano nello stesso spazio di indirizzamento, altrimenti non sarebbero concorrenti e la concorrenza dovrebbe essere stabilita tramite meccanismi di comunicazione interprocesso (vedi pipe, fifo, mailslot etc).
Si, ma credo che almeno gli stack debbano essere separati. Mi sembra inverosimile che usino lo stesso stack.
Molto , molto strano
Conosciamo il problema ma non sappiamo proprio come risolverlo, speriamo che a qualcuno venga una bella ideona, se no... :cry:
Io non ho ancora capito qual'è il problema...o meglio...come la questione vada ad influire sul vostro codice. Se influisce significa che non avete inizializzato delle variabili.
I thread per definizione condividono lo spazio di indirizzamento del processo/thread che li crea. Quindi di qui non si scappa. Due thread concorrenti girano nello stesso spazio di indirizzamento, altrimenti non sarebbero concorrenti e la concorrenza dovrebbe essere stabilita tramite meccanismi di comunicazione interprocesso (vedi pipe, fifo, mailslot etc).
D'accordissimo, però tra le tante definizioni legate ai threads ce n'è anche una che dice(postata nel mio primo reply a questo argomento):
Le variabili che vengono dichiarate localmente all'interno della funzione che implementa il codice di ciascun thread sono private di quello specifico thread, e pertanto non accessibili da parte degli altri thread del processo.
E questo sembra non accadere.........
Boh, non avessi provato con le mie mani ogni caso, non avessimo riscritto da 0 codice apposta per fare tests, non fossi sicuro d'avere chiara la situazione e soprattutto non fossi sicuro d'aver scritto un codice senza bugs, non starei qui a whinare come una bestia.... LOL :cry:
Si, ma credo che almeno gli stack debbano essere separati. Mi sembra inverosimile che usino lo stesso stack.
Molto , molto strano
Gli stack sono separati. Sono istanze consecutive dello stesso thread...loro non fanno girare due thread contemporaneamente, ma ne fanno girare uno e poi ne fanno partire un altro quando il primo è terminato ;)
Io non ho ancora capito qual'è il problema...o meglio...come la questione vada ad influire sul vostro codice. Se influisce significa che non avete inizializzato delle variabili.
E' tutto spiegato qui nell'ultimo esempio: http://www.hwupgrade.it/forum/showpost.php?p=19507487&postcount=19
Meglio di così non riesco proprio a spiegarlo..
E questo sembra non accadere.........
Non accade perché voi utilizzate istanze consecutive ed in quanto tali vanno a riutilizzare aree di memoria dove il thread precedente era allocato...
Gli stack sono separati. Sono istanze consecutive dello stesso thread...loro non fanno girare due thread contemporaneamente, ma ne fanno girare uno e poi ne fanno partire un altro quando il primo è terminato ;)
Si, nell'esempio banale della x non inizializzata, atto a dimostrare che esiste una INSPIEGABILE condivisione di variabili locali tra threads.
Come spiegato qui (http://www.hwupgrade.it/forum/showpost.php?p=19507487&postcount=19), nell'esempio "Esempio con 3 threads che arrivano in maniera concorrenziale(conteporeaneamente)" c'è un problema concorrenziale dal quale non riusciamo ad uscire.
AnonimoVeneziano
06-11-2007, 19:11
Forse ci siamo.
Dovete creare una variabile (nel main) di tipo "pthread_t" per ogni thread che volete lanciare in concorrenza
Quindi se volete mandare al massimo 10 thread contemporaneamente in esecuzione fate una cosa tipo :
pthread_t threads[10];
pthread_create(&threads[x], ....); <--- dove X è il numero del thread che volete lanciare.
Non riutilizzate sempre lo stesso.
Ciao
Non accade perché voi utilizzate istanze consecutive ed in quanto tali vanno a riutilizzare aree di memoria dove il thread precedente era allocato...
No, attenzione. Ti sei focalizzato sull'esempio della x che abbiamo fatto all'inizio. Forse colpa nostra che l'abbiamo spiegato male, forse che cmq non è un argomento semplicissimo da debuggare, tantopiù attraverso un forum, questo non lo so.
Noi l'abbiamo fatto diversi test concorrenziali e il risultato è quello che ho scritto qui (http://www.hwupgrade.it/forum/showpost.php?p=19507487&postcount=19) nell'ultimo esempio.
Forse ci siamo.
Dovete creare una variabile (nel main) di tipo "pthread_t" per ogni thread che volete lanciare in concorrenza
Quindi se volete mandare al massimo 10 thread contemporaneamente in esecuzione fate una cosa tipo :
pthread_t threads[10];
pthread_create(&threads[x], ....); <--- dove X è il numero del thread che volete lanciare.
Non riutilizzate sempre lo stesso.
Ciao
Eurekà! Mi fa piacere che qualcuno è riuscito a capire sto casino che ho messo in piedi, LOL.. :)
Abbiamo provato ad adottare anche questa soluzione ma ci siamo bloccati per un "piccolo" motivo: quello che questo programma è un daemon sempre in esecuzione, come facciamo a creare un array finito di threads quando le richieste sono tecnicamente infinite? La soluzione "numero molto grosso" è troppo sporca ed inefficente ed è stata scartata.
Ma in ogni caso, se questa è effettivamente la soluzione al nostro problema dovremmo creare una lista di threads, ma poi, come la andiamo a gestire? :eek:
nono spe, mi son perso un bel po di reply...grazie a tutti per le risposte innanzitutto....dunque, i thread creabili non sono in numero finito ne prederminabile in alcun modo; posso pensare che da questo stato di cose non sia facile uscire facilmente...va a finire che dovremo reinizializzare le variabili ogni volta e infilare l intero codice dei thread entro un mutex (cioe cio che ci hanno chiesto di non fare)...sinceramente non vedo altre soluzioni per evitare che qualcosa dentro un thread sia sporcata da qualcos altro di un altro thread...spero di essermi spiegato
jappilas
06-11-2007, 19:36
x è locale a quell'istanza del thread...ogni thread ha la sua x che viene creata al momento della creazione del thread e distrutta al momento che la funzione del thread termina la sua esecuzione. In teoria gli altri thread potrebbero però accedere a x conoscendone l'indirizzo.sì ma stando alla documentazione (http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html) pare che GCC non consideri "automaticamente" le variabili dichiarate normalmente all' inizio della thread-loop function, come thread-local storage - che è quel che a loro serve dal momento che ogni thread deve avere la propria istanza completamente indipendente della variabile, in questo caso intera, x
e per istruire il compilatore all' uso del TLS sarebbe prevista una keyword apposita
__thread int x;
tomminno
06-11-2007, 19:38
Ma scusate potete postare un pezzo di codice reale in cui si manifesta questo problema?
Perchè se inizializzate tutte le variabili e dopo l'inizializzazione queste risultano corrotte ci deve essere un problema da qualche altra parte.
Non ho capito questo esempio:
SIMULAZIONE POSSIBILE:
1) creazione ilnostrothread_1.
2) inizio computazione ilnostrothread_1, int x = 0, sleep, context switch.
3) creazione ilnostrothread_2.
4) inizio computazione ilnostrothread_2, int x = 0, sleep, context switch.
5) creazione ilnostrothread_3.
6) inizio computazione ilnostrothread_3, int x = 0, sleep, context switch.
7) proseguimento computazione ilnostrothread_1, x++, x = 1, "1", pthread_exit.
8) proseguimento computazione ilnostrothread_1, x++, x = 2, "2", pthread_exit.
9) proseguimento computazione ilnostrothread_1, x++, x = 3, "3", pthread_exit.
Questo è quello che volete ottenere o quello che NON volete si ottenga ?
L'output in questo caso è ovviamente 1, 1, 1
Non ho capito questo esempio:
SIMULAZIONE POSSIBILE:
1) creazione ilnostrothread_1.
2) inizio computazione ilnostrothread_1, int x = 0, sleep, context switch.
3) creazione ilnostrothread_2.
4) inizio computazione ilnostrothread_2, int x = 0, sleep, context switch.
5) creazione ilnostrothread_3.
6) inizio computazione ilnostrothread_3, int x = 0, sleep, context switch.
7) proseguimento computazione ilnostrothread_1, x++, x = 1, "1", pthread_exit.
8) proseguimento computazione ilnostrothread_1, x++, x = 2, "2", pthread_exit.
9) proseguimento computazione ilnostrothread_1, x++, x = 3, "3", pthread_exit.
Questo è quello che volete ottenere o quello che NON volete si ottenga ?
L'output in questo caso è ovviamente 1, 1, 1
Questo è quello che otteniamo e che NON vogliamo si ottenga.
Comunque l'output è scritto e si ottiene "1", "2", "3", magari fosse "ovviamente 1, 1, 1"... :)
Agli altri 2 reply rispondo domani che ora sono proprio fuso. Grazie a tutti dell'aiuto che ci state dando:oink:
Allora avete un'implementazione fallata dei pthread...non c'è altra spiegazione:
#include <stdio.h>
#include <pthread.h>
void *miothread(void * arg)
{
int x = 0;
sleep(1);
printf("%d\n", ++x);
pthread_exit(0);
}
int main()
{
pthread_t t;
pthread_create(&t, NULL, miothread, NULL);
pthread_create(&t, NULL, miothread, NULL);
pthread_create(&t, NULL, miothread, NULL);
sleep(2);
return 0;
}
~$ gcc -lpthread prova.c -o prova
~$ ./prova
1
1
1
Su che piattaforma siete ?
In ogni caso l'esempio senza inizializzazione non è stato solo fuorviante, ma proprio non corretto in quanto il problema di base è diverso.
Ma scusate avete provato a verificare che indirizzi fisici prendono le variabili??
Io son dell'idea di qualche errore nel codice...non me ne vogliate eh ;)
AnonimoVeneziano
07-11-2007, 17:47
In ogni caso l'esempio senza inizializzazione non è stato solo fuorviante, ma proprio non corretto in quanto il problema di base è diverso.
Non c'entra un tubo , ma sto ascoltando Spirit Crusher .... RIP per l'avatar :cry:
Comunque anche a me da il risultato corretto sul codice che hai postato.
A sto punto non ho capito se è DAVVERO necessario o no che ogni thread abbia la sua variabile "pthread_t" o meno. Magari il fatto che così funziona è solo dato dal caso che tutti i thread finiscano prima che quello successivo venga spawnato .
Ciao
Che io sappia non è necessario...
:ave:
AnonimoVeneziano
07-11-2007, 18:06
Che io sappia non è necessario...
Il fatto è che su tutti gli esempi che ho trovato viene usata una variabile per OGNI thread lanciato, tipo qua : http://www.llnl.gov/computing/tutorials/pthreads/
Il fatto è che su tutti gli esempi che ho trovato viene usata una variabile per OGNI thread lanciato, tipo qua : http://www.llnl.gov/computing/tutorials/pthreads/
Che io sappia, come Cionci, non è necessario che ogni thread abbia la sua variabile. Alla fine si utilizzano vettori o simili giusto per controllare meglio i thread tramite id ma nulla di piu'.
Ogni thread avrà in ogni caso il suo id e, come da teoria che ci sta sotto, le proprie variabili, stack, file ecc.
Io ho provato a creare diversi tipi di thread, giusto per farli sovrapporre, e non ho riscontato problemi.
Imho è solo un riferimento. Ovviamente se devi usare il join il riferimento ti serve, se non lo usi il riferimento lo puoi tranquillamente lasciar perdere ;)
AnonimoVeneziano
07-11-2007, 20:34
Allora a sto punto diventa fondamentale sapere che implementazione dei pthread stanno usando e su che SO, perchè rimane l'unica cosa da incolpare :D
Ciao
solaris 10...
abbiamo "risolto", nel senso che mallochiamo anche il piu piccolo spillo e inizializziamo qualsiasi cosa ogni volta che si deve usare...ora sono usciti altri problemi ancora piu stronzi e impensabili, ma per il cercheremo di fixare da soli...nonappena mi accorgero di non farcela, se trovero il modo di formularlo in italiano comprensbile, provero a postare...
...il succo del problema che abbiamo esposto finora era che allocando un nuovo thread dopo che l altro era morto (e non reinizializzando le variabili), questo si trovava a pescare gli indirizzi -anche- delle variabili locali del thread precedente che ovviamente hanno un loro valore...brutto, si, antipatico anche, ma non del tutto incredibile, ne sbagliato tra l altro...è solo la politica seguita sal so e siam stati piciu noi a non inizializzare tutto ogni volta...
Non c'entra un tubo , ma sto ascoltando Spirit Crusher .... RIP per l'avatar :cry:
Comunque anche a me da il risultato corretto sul codice che hai postato.
A sto punto non ho capito se è DAVVERO necessario o no che ogni thread abbia la sua variabile "pthread_t" o meno. Magari il fatto che così funziona è solo dato dal caso che tutti i thread finiscano prima che quello successivo venga spawnato .
Ciao
Diciamo di si'. Perche' il thread figlio quando finisce diventa uno zombie finche' non ci fai la join per recuperare il risultati, per cui ti rimane occupata tutta la memoria relativa a quel thread. In alcuni casi potrebbe trattarsi di parecchia roba.
Si, diciamo che mi sono spiegato male, lo so che viene inizializzata random nel range dell'int, però in questo caso abbiamo fatto una comunissima prova ed al primo passaggio i++ printa "1", quindi si, dipende dal compilatore ma questo poco ci interessa.
Se, ad esempio, la inizializzassimo OGNI volta a 0, esempio:
void* ilnostrothread() {
[...]
int x = 0;
x++;
printf("%d\n", x);
[...]
pthread_exit(NULL);
}
Ad ogni chiamata di questo thread avremo sempre printato "1". Quindi, senza ragionarci su, potremmo dire che per risolvere il problema iniziale basterebbe inizializzare a 0/NULL tutte le variabili di cui faremo uso all'interno del thread.
Questo si, andrebbe bene nel caso in cui lanciassimo solo un thread dello "stesso tipo"( pthread_create(&T, NULL, ilnostrothread, NULL); ) alla volta aspettando la sua terminazione con il pthread_join nel main oppure con un mutex che tenga lockato il thread in tutta la sua computazione. Così abbiamo già appurato che funziona, però viene a mancare tutta la parte concorrenziale del progetto.
Finalmente sono riuscito a spiegare il problema? Scusami ma spiegarlo è più difficile di quel che sembra.. :)
Francamente, dopo aver letto il thread almeno tre volte, devo ancora capire se volete che questi thread condividano o no il contenuto di quelle variabili. :confused:.
Se non devono farlo niente di piu' semplice: le inizializzate ed ogni thread avra' il valore iniziale previsto. Ovviamente se non inizializzate può essere che un nuovo thread riutilizzi la memoria del precedente per cui vedete i valori precedenti, ma questo e' un caso fortuito dovuto al fatto che c'è un errore nel codice (variabile non inizializzata).
Se dovete scambiare valori tra variabili non potete usare variabili locali ai thread. Dichiarate la variabile fuori dal thread e la passate come argomento durante la pthread_create. Ad esempio
void *miothread(void * arg)
{
int& x = * static_cast<int*>(arg);
sleep(1);
printf("%d\n", ++x);
pthread_exit(0);
}
int main()
{
volatile int x = 0;
pthread_t t1;
pthread_t t2;
pthread_create( &t1, NULL, miothread, (void*)&x );
pthread_create( &t2, NULL, miothread, (void*)&x );
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
A scanso di equivoci ripero: del fatto che vengano ripescati i vecchi indirizzi tra un thread e l'altro non ve ne deve fregare niente, ve ne accorgete solo se non inizializzate le variabili e quello e' un errore di programmazione.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.