2012-06-04 18 views
6

il problema è nel seguente:Usando della variabile condivisa dal 10 pthreads

Voglio scrivere un breve programma che crea 10 thread e ogni stampa un battistrada "id" che viene passata a infilare funzione puntatore.

codice completo del programma è qui sotto:

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 

struct params { 
     pthread_mutex_t mutex; 
     int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 
    int id; 
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex); 
    id = (*(params_t*)(arg)).id; 
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex); 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      params.id = i; 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

L'uscita presunta è (non è necessario in questo ordine):

Hello from 0 
.... 
Hello from 9 

risultato Attuale è:

Hello from 2 
Hello from 3 
Hello from 3 
Hello from 4 
Hello from 5 
Hello from 6 
Hello from 8 
Hello from 9 
Hello from 9 
Hello from 9 

I ho provato a posizionare mutex in diversi punti nella funzione hello(), ma non è stato di aiuto.

Come devo implementare la sincronizzazione dei thread?

MODIFICA: Il risultato non è necessario 0 ... 9 può essere una qualsiasi combinazione di questi numeri, ma ognuno deve apparire solo una volta.

risposta

2

ci sono due problemi:

A. Si sta utilizzando un lock ma main è a conoscenza di questo blocco.

B. A lock non è sufficiente in questo caso. Quello che vorresti è che i thread cooperino segnalandoti a vicenda (perché vuoi che main a non incrementa la variabile fino a quando un thread dice che è stato fatto stamparlo). È possibile utilizzare un pthread_cond_t per ottenere questo (Look here per ulteriori informazioni su questo). Questo riduce al seguente codice (in pratica, ho aggiunto un uso appropriato di pthread_cond_t al codice, e un mazzo di commenti per spiegare cosa sta succedendo):

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 

struct params { 
     pthread_mutex_t mutex; 
     pthread_cond_t done; 
     int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 

    int id; 
    /* Lock. */ 
    pthread_mutex_lock(&(*(params_t*)(arg)).mutex); 

    /* Work. */ 
    id = (*(params_t*)(arg)).id; 
    printf("Hello from %d\n", id); 

    /* Unlock and signal completion. */ 
    pthread_mutex_unlock(&(*(params_t*)(arg)).mutex); 
    pthread_cond_signal (&(*(params_t*)(arg)).done); 

    /* After signalling `main`, the thread could actually 
    go on to do more work in parallel. */ 
} 


int main() { 

    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 
    pthread_cond_init (&params.done, NULL); 

    /* Obtain a lock on the parameter. */ 
    pthread_mutex_lock (&params.mutex); 

    int i; 
    for(i = 0; i < 10; i++) { 

      /* Change the parameter (I own it). */  
      params.id = i; 

      /* Spawn a thread. */ 
      pthread_create(&threads[i], NULL, hello, &params); 

      /* Give up the lock, wait till thread is 'done', 
      then reacquire the lock. */ 
      pthread_cond_wait (&params.done, &params.mutex); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    /* Destroy all synchronization primitives. */  
    pthread_mutex_destroy (&params.mutex); 
    pthread_cond_destroy (&params.done); 

    return 0; 
} 

Vedo che l'esempio si sta cercando è un programma giocattolo per conoscere probabilmente la libreria di thread POSIX. Nel mondo reale, come tutti sappiamo, questo può essere fatto molto più velocemente senza nemmeno usare i thread. Ma lo sai già.

1

Il problema è che si sta modificando il params.id "non protetto" in main. Anche questa modifica in main deve essere protetta da mutex. È possibile proteggere questo accesso localizzando questo creando funzioni getId() e setId() che blocchino il mutex e proteggano l'accesso all'id, come segue. Molto probabilmente questo ancora darà il problema segnalato, poiché a seconda di quando il thread chiama getData() avrà un valore o un altro. Quindi, per risolvere questo, potresti aggiungere una funzione incrementId() e chiamarla dalla funzione ciao().

struct params { 
     pthread_mutex_t mutex; 
     int id; 
}; 

typedef struct params params_t; 

int getId(params_t *p) 
{ 
    int id; 
    pthread_mutex_lock(&(p->mutex)); 
    id = p->id; 
    pthread_mutex_unlock(&(p->mutex)); 

    return id; 

} 

void setId(params_t *p, int val) 
{ 
    pthread_mutex_lock(&(p->mutex)); 
    p->id = val; 
    pthread_mutex_unlock(&(p->mutex)); 
} 

void incrementId(params_t *p) 
{ 
    pthread_mutex_lock(&(p->mutex)); 
    p->id++; 
    pthread_mutex_unlock(&(p->mutex)); 
} 

void* hello(void* arg){ 
    params_t *p = (params_t*)(arg); 
    incrementId(p); 
    int id = getId(p); 

    // This could possibly be quite messy since it 
    // could print the data for multiple threads at once 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t params; 
    params.id = 0; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
    } 

    for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

Un modo migliore per ottenere un filo unico id sarebbe quello di definire il metodo ciao come segue:

void* hello(void* arg){ 
    pthread_t threadId = pthread_self(); 
    printf("Hello from %d\n", threadId); 
} 

E per evitare il problema con tutte le discussioni si tenta di stampare in una volta, si potrebbe fare la seguente:

void* hello(void* arg){ 
    params_t *p = (params_t*)(arg); 
    pthread_mutex_lock(&(p->mutex)); 

    p->id++; 
    int id = p->id; 
    printf("Hello from %d\n", id); 

    pthread_mutex_unlock(&(p->mutex)); 
} 
+0

Anche con l'utilizzo suggerito da voi, l'OP potrebbe ancora ottenere gli stessi risultati che sta ottenendo ora. Mi sto perdendo qualcosa? – Jay

+0

@ Jay, ho aggiornato il codice per mostrare meglio l'utilizzo. Ma hai ragione, ogni thread non ha il suo valore e il valore che hanno dipende quando chiamano getId() – Brady

+0

@Jay, ho trovato un modo migliore per farlo senza dover creare un'istanza param per thread: solo incrementare l'id dall'interno del thread. – Brady

7

il problema sta nel seguente codice:

for(i = 0; i < 10; i++) 
{    
    params.id = i;    
if(pthread_create(&threads[i], NULL, hello, &params));  
} 

Il valore params.id continua ad essere aggiornato nel thread principale, mentre si passa lo stesso puntatore a tutti i thread.

Si prega di creare memoria separata per params allocandolo dinamicamente e passarlo a thread diversi per risolvere il problema.

EDIT1: Anche l'utilizzo di mutex da proteggere è un'idea errata. Anche se il tuo mutex se usato in main mentre si imposta anche l'id, può rendere l'aggiornamento mutuamente esclusivo, ma potresti non ottenere l'output desiderato. Invece di ottenere valori da 0 a 9 in thread diversi, è possibile ottenere tutti gli 9 o ancora più thread potrebbero stampare gli stessi valori.

Quindi, utilizzare la sincronizzazione dei thread non è una buona idea per l'output che si aspetta. Se è ancora necessario utilizzare una variabile param tra tutti i thread e ottenere l'output da 0 a 9 da ciascuno dei thread, spostare il file pthread_join nel primo ciclo.Questo assicurerà che ogni thread venga creato, stampi il valore e poi ritorni prima che il main produca il thread successivo. In questo caso, non è necessario anche il mutex.

EDIT2: Per quanto riguarda la domanda aggiornato, in cui si chiede che non è necessario per stampare i numeri 0..9 in una sequenza, la stampa può essere casuale, ma solo una volta, il problema rimane ancora lo stesso più o meno.

Ora, diciamo, il valore di params.id è il primo 0 e il thread 0 è stato creato, ora il thread 0 deve stamparlo prima che venga aggiornato nel thread principale, altrimenti, quando il thread 0 lo accede, il valore di params.id sarebbe diventato 1 e non otterrai mai il tuo insieme di valori unico. Così, come garantire che filo 0 stampe prima che viene aggiornato in principale, due modi per esso:

  • Assicurarsi filo 0 completa l'esecuzione e la stampa prima che gli aggiornamenti principali il valore
  • variabili Usa condizione & segnalazione a assicurare che thread principale attende per filo 0 per completare la stampa prima di aggiornare il valore (Fare riferimento alla risposta di Arjun sotto per maggiori dettagli)

a mio parere onesto, è stato selezionato il problema sbagliato per l'apprendimento sincrono ization & memoria condivisa. Puoi provare questo con alcuni buoni problemi come "Producer-Consumer", in cui hai davvero bisogno di sincronizzazione per far funzionare le cose.

+0

Potrei usare la memoria separata, ma l'idea principale era nella variabile condivisa. Comunque grazie. – Alex

+0

@ Jay - Questa è una buona risposta. Tuttavia, c'è un modo per farlo correttamente senza usare un 'join' in loop, e senza usare memoria separata. ad esempio, usi 'pthread_cond_t'. Mentre si tratta di un programma giocattolo, il segnale tra i thread che utilizza una variabile di condizione mi sembra il modo * giusto * per questo programma giocattolo (e l'OP deve imparare qualcosa di nuovo). Controlla la mia risposta. – ArjunShankar

+0

@ArjunShankar riguardo al programma è un programma davvero giocattolo per capire come funzionano i pthread. – Alex

0

modo più semplice per ottenere il risultato desiderato potrebbe essere quella di modificare la funzione main come segue:

int main() { 
    pthread_t threads[10]; 
    params_t params; 
    pthread_mutex_init (&params.mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
      params.id = i; 
      if(pthread_create(&threads[i], NULL, hello, &params)); 
      pthread_join(threads[i], NULL); //wait for thread to finish 
    } 

    /*for(i = 0; i < 10; i++) { 
      pthread_join(threads[i], NULL); 
    }*/ 

    return 0; 
} 

uscita sarebbe:

Hello from 0 
... 
Hello from 9 

EDIT: Ecco la sincronizzazione per la domanda corretta:

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 

struct params { 
    pthread_mutex_t* mutex; 
    int id; 
}; 

typedef struct params params_t; 

void* hello(void* arg){ 
    int id = 0; 
    params_t* params = (params_t*)arg; 
    if(params != 0) 
    { 
     id = params->id; 
     delete params; 
     params = 0; 
    } 
    printf("Hello from %d\n", id); 
} 


int main() { 
    pthread_t threads[10]; 
    params_t* params = 0; 
    pthread_mutex_t main_mutex; 
    pthread_mutex_init (&main_mutex , NULL); 

    int i; 
    for(i = 0; i < 10; i++) { 
     params = new params_t(); //create copy of the id to pass to each thread -> each thread will have it's own copy of the id 
     params->id = i; 
     params->mutex = &main_mutex; 
     if(pthread_create(&threads[i], NULL, hello, params)); 
    } 

    for(i = 0; i < 10; i++) { 
     pthread_join(threads[i], NULL); 
    } 

    return 0; 
} 

Ogni thread deve avere la propria copia dell'ID in modo che gli altri thread non modifichino l'id prima che venga stampato.

+0

Funzionerebbe per thread di breve durata, ma non se l'OP vuole che i thread durino più a lungo. E i nuovi thread non verranno creati fino al completamento dei thread precedenti. – Brady

+0

Ho appena letto la domanda. La soluzione si applica all'attuale affermazione del problema. Non è pensato per essere generico. –

+0

@AlexandruC. In questo caso hai ragione, funzionerà ed è la soluzione più semplice, ma in questo caso non ho nemmeno bisogno di un array di 'pthread_t'. Ho solo bisogno di una variabile con questo tipo. – Alex

0

sto solo mettendo questo uno qui per fornire un'altra soluzione a questo problema - questo non comporta mutex - nessuna sincronizzazione - non condizionali, ecc Il dfference principale è che stiamo usando pthread_detach di rilasciare automaticamente il filo di risorse al termine.

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

#define NUMTHREADS 10   

typedef struct params {  
    int id;  
} params_t;                  

void* hello(void *arg) 
{ 
    params_t *p = (params_t*)arg;  
    int status; 
    status = pthread_detach(pthread_self());  
    if (status !=0)  
    {  
     printf("detaching thread\n");  
     abort();  
    }                   

    printf("Hello from %d\n", p->id); 
    free(p);  
    return NULL; 
}      

int main() 
{ 
    pthread_t thread;  
    params_t *par;  
    int i, status;                
    for (i=0; i<NUMTHREADS; i++)  
    {  
     par = (params_t*)malloc(sizeof(params_t));  
     if (par == NULL)  
     {  
      printf("allocating params_t"); 
      abort();  
     }                  

     par->id = i; 
     status = pthread_create(&thread, NULL, hello, par); 
     if (status != 0)  
      exit(1);  
    }  
    /* DO some more work ...*/              
    sleep(3);                   
    exit(0); 
} 
Problemi correlati