POSIX Threads

Una semplice applicazione C++ per illustrare le basi della programmazione multithreading secondo le specifiche POSIX.

Molti dettagli sono dipendenti dall'implementazione, in questo caso lo sviluppo é stato fatto su Cygwin.

Disegnamo una classe, CountDown, che permette di fare il conto alla rovescia.

Dal main vediamo come viene usata, si istanzia un oggetto della classe e si chiama su di esso il metodo run():

CountDown cd1("two x two", 2, 2);
// ...
cd1.run();

Notiamo che tra i membri della classe abbiamo messo un oggetto di tipo pthread_t che rappresenta l'identificatore del thread in cui viene eseguita:

pthread_t m_thread_id;

Il metodo run() crea un nuovo thread salvando nella variabile membro m_thread_id l'identificatore del thread creato e passando l'indirizzo della metodo privato go() che cura i dettagli implementativi della classe.

pthread_create(&m_thread_id, NULL, &go, this)

Il metodo go() é dichiarato static per ragioni di compatibilità con pthread, che é una libreria C e quindi nulla sa di classi e oggetti.

La prima cosa che facciamo in go() é rendere disponibile l'accesso alla classe su cui stiamo lavorando. Il "trucco" a cui abbiamo fatto ricorso é quello di passare this dalla run() via pthread_create come puntatore a void, e qui farlo tornare un puntatore a CountDown con un (piuttosto brutale) static_cast.

CountDown* cd = static_cast(ptr);

E quindi viene eseguito in count down usando i settaggi per l'oggetto corrente. Notiamo l'uso della funzione sleep() per scandire il tempo:

sleep(cd->m_sleep);

Per evitare che un oggetto venga distrutto quando il suo thread é ancora in corso di esecuzione, facciamo una join (ovvero, attendiamo la terminazione del thread) nel distruttore:

pthread_join(m_thread_id, 0);

E questo é, più o meno, tutto. A seguire il codice completo:

#include <string>
#include <iostream>
#include <unistd.h>
#include <errno.h>
#include <sstream>

using namespace std;

class CountDown {
public:
CountDown(const char* id, int start, int sleep =1) :
m_id(id), m_start(start), m_sleep(sleep), m_thread_id(0) {}

~CountDown();

void run();

private:
static void* go(void*);

string m_id;
int m_start;
int m_sleep;
pthread_t m_thread_id;
};

// ensure the object is destroyed only when the thread is terminated
CountDown::~CountDown() {
pthread_join(m_thread_id, 0);
cout << "Dtor completed on " << m_id << endl;
}

// notice: should be static for pthread requirements
void* CountDown::go(void* ptr) {
CountDown* cd = static_cast<CountDown*>(ptr);

ostringstream os;
os << "running " << cd->m_id << endl;
cout << os.str();

for(int i = cd->m_start; i > 0; --i) {
os.str("");
os << cd->m_id << " " << i << endl;
cout << os.str();
sleep(cd->m_sleep);
}

cout << "count down done for " << cd->m_id << endl;
return 0;
}

void CountDown::run() {
if(pthread_create(&m_thread_id, NULL, &go, this) < 0) {
cout << "Error creating thread " << errno << endl;
return;
}

ostringstream os;
os << "Thread id " << m_thread_id << " created (" << m_id << ")" << endl;
cout << os.str();
}

/*
*
*/
int main(int argc, char** argv) {
CountDown cd1("two x two", 2, 2);
CountDown cd2("three", 3);
CountDown cd3("four", 4);

cd1.run();
cd2.run();
cd3.run();

ostringstream os;
os << "All threads are running" << endl;
cout << os.str();
}

Nessun commento:

Posta un commento