C – POSIX Thread

C – POSIX Thread: è un modello di esecuzione che consente a un programma di controllare più flussi di lavoro diversi che si sovrappongono nel tempo.

Creazione di un pthread

La creazione di un Pthread è effettuata con la funzione:

int pthread_create ( pthread_t *threadId , const pthread_attr_t *attr, void (*start_routine) (void *), void *arg );

• threadId è l’indirizzo di una variabile di tipo identificatore di Pthread in cui verrà messo l’identificatore del Pthread creato.

• attr è l’indirizzo di una variabile di tipo pthread_att_t in cui posso specificare alcune caratteristiche del Pthread da creare. Il parametro attr può essere NULL per usare le impostazioni di default.

• start_routine è il nome della funzione che implementa il Pthread da creare. Tale funzione formalmente deve prendere come argomento un puntatore generico void* e deve restituire un puntatore generico void*

• arg è un puntatore generico void* che punta ad un’area di memoria in cui sono stati messi gli argomenti da passare alla funzione start_routine da eseguire. Il formato di tale area di memoria è user defined, cioè è concordato tra chi implementa il Pthread e chi implementa la chiamata al Pthread. Il parametro arg può essere NULL se voglio creare un Pthread senza passargli alcun argomento.

• Il risultato restituito dalla funzione pthread_create è un intero. Vale 0 se la funzione è riuscita a creare il Pthread, differentemente vale !=0, se non è stato possibile creare il Pthread.

Esempio creazione thread

#include "stdio.h";
#include<stdlib.h>
#include "pthread.h";
#define NUM_THREADS 10
pthread_t tid;
void * func( void *arg );
int main(){
    int t, rc;
    int *p;
    for(t=0;t<NUM_THREADS;t++){
        p = (int *) malloc(sizeof(int));
        *p = t;
        rc = pthread_create( & tid , NULL, func, (void *) p );
    }
}
void * func( void *arg ){
    int indice;
/* uso arg come puntatore a int */
    indice = * ((int*)arg);
/* rilascio memoria */
    free( arg ); /* non fare in caso sbagliato */
    printf(" ho ricevuto %i \n", indice );
    pthread_exit( NULL );
}

Attesa di terminazione di un Pthread

Un join permette ad un thread di sospendere la propria esecuzione in attesa che un altro thread termini la propria esecuzione.

Effettivamente un processo, in fase di inizializzazione, crea diversi thread (ognuno con uno scopo ben preciso) e si pone in attesa del loro completamento. La chiamata di sistema da utilizzare per il join è:

int pthread_join(pthread_t th,void **thread_return);

Nel caso in cui come secondo argomento non si specifichi NULL, il valore di ritorno del thread th specificato tramite la funzione pthread_exit, viene memorizzato nella locazione puntata da thread_return.

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
void print_msg(void *ptr);
int main() {
    pthread_t thread1,thread2;
    char *msg1="Thread 1";
    char *msg2="Thread 2";
    if(pthread_create(&thread1,NULL,(void *)&print_msg,(void *)msg1)!=0) {
        perror("Errore nella creazione del primo thread.\n");
        exit(1);
    }
    if(pthread_create(&thread2,NULL,(void *)&print_msg,(void *)msg2)!=0) {
        perror("Errore nella creazione del secondo thread.\n");
        exit(1);
    }
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    exit(0);
    return 0;
}
void print_msg(void *ptr) {
    printf("%s\n",(char *)ptr);
}

Sincronizzazione di thread

Una condition variable è una variabile di tipo pthread_cond_t che viene utilizzata per sospendere l’esecuzione di un thread ,in attesa che si verifichi un certo evento, praticamente va sempre utilizzata associandola ad un mutex per evitare che si verifichino problemi di deadlock.

Così si inizializza una condition variable tramite la seguente sintassi: pthread_cond_t cond=PTHREAD_COND_INITIALIZER;

Affinché un thread vada in attesa si può utilizzare una delle seguenti system call:

int pthread_cond_wait(pthread_cond_t *cond,pthread_mutex_t *mutex);

int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex, const struct timespec *abstime);

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

pthread_mutex_t condition_mutex=PTHREAD_MUTEX_INITIALIZER; /* mutex */
pthread_cond_t condition_cond=PTHREAD_COND_INITIALIZER;    /* condition variable */
void thread1_func(void *ptr); /* funzione eseguita dal primo thread */
void thread2_func(void *ptr); /* funzione eseguita dal secondo thread */
int main() {
    pthread_t thread1,thread2;
    char *msg1="Thread 1";
    char *msg2="Thread 2";
    if(pthread_create(&thread1,NULL,(void *)&thread1_func,(void *)msg1)!=0) {
        perror("Errore nella creazione del primo thread.\n");
        exit(1);
    }
    if(pthread_create(&thread2,NULL,(void *)&thread2_func,(void *)msg2)!=0) {
        perror("Errore nella creazione del secondo thread.\n");
        exit(1); }
    pthread_join(thread1,NULL);
    pthread_join(thread2,NULL);
    exit(0);
}
void thread1_func(void *ptr) {
    printf("Avvio dell’esecuzione del %s.\n",(char *)ptr);
    sleep(2); /* pausa di 2 secondi */
    printf("Thread 1 in procinto di entrare nella sezione critica.\n");
    pthread_mutex_lock(&condition_mutex);
    printf("Thread 1 nella sezione critica.\n");
    printf("Thread 1 si sospende sulla condition variable.\n");
    pthread_cond_wait(&condition_cond, &condition_mutex);
    printf("Thread 1 riprende l’esecuzione.\n");
    printf("Thread 1 in procinto di uscire dalla sezione critica.\n");
    pthread_mutex_unlock(&condition_mutex);
    printf("Thread 1 in procinto di terminare.\n");
}
void thread2_func(void *ptr) {
    printf("Avvio dell’esecuzione del %s.\n",(char *)ptr);
    sleep(5); /* pausa di 5 secondi */
    printf("Thread 2 in procinto di entrare nella sezione critica.\n");
    pthread_mutex_lock(&condition_mutex);
    printf("Thread 2 nella sezione critica.\n");
    printf("Thread 2 segnala l’evento della condition variable.\n");
    pthread_cond_signal(&condition_cond);
    printf("Thread 2 in procinto di uscire dalla sezione critica.\n");
    pthread_mutex_unlock(&condition_mutex);
    printf("Thread 2 in procinto di terminare.\n");
}

Pubblicato da Carlo Contardi

Carlo Contardi, docente di informatica e sviluppatore Full Stack, condivide la sua passione per la programmazione e l’informatica attraverso il suo blog Space Coding. Offre preziosi consigli e soluzioni pratiche a chi vuole imparare a programmare o migliorare le proprie abilità. 🚀👨‍💻

Translate »