Le macro di gestione dei thread di CLB_thread
In quest’ultima parte dell’articolo introdurremo un insieme di macro, appartenenti a CLB_lang, che semplificano la gestione ed attivazione dei thread associati alle classi CLB_thread. Abbiamo già osservato che le classi CLB_thread non espongono direttamente la dichiarazione delle funzioni thread eseguite, ma solo un insieme di funzioni e macro, valide per qualunque classe derivata, che ne permettono la gestione in termini di lancio, sospensione, interruzione e stato di attività. Vediamo con un esempio reale, scritto all’interno di Colibri: la classe THREAD_calc_dither.
THREAD_calc_dither è una classe che riceve in ingresso un file da retinare ed altri parametri utilizzati per la retinatura, eseguendo internamente un thread. La procedura chiamante, a fine esecuzione potrà richiedere il file retinato attraverso la funzione Get_dithered_map(). . Non discutiamo del risultato del procedimento, ma solo sul modo in cui i parametri sono passati alla classe THREAD_calc_dither e come internamente vengono usati.
Di seguito la dichiarazione dell classe:
class AFX_EXT_CLASS THREAD_calc_dither : public CLB_thread { public: THREAD_calc_dither(void); ~THREAD_calc_dither(void); virtual bool Start(); IMG_FBUF *Get_dithered_map(); __use_private_struct };
Come si osserva, la classe espone solo due funzioni:
- Start() usata per lanciare il thread interno
- Get_dithered_map() per estrarre il risultato a fine processo
La Start è la riscrittura della classe virtuale CLB_Thread, che, come abbiamo scritto è necessario riscrivere per le classi derivate;
La classe THREAD_calc_dither è usata in una funzione che esegue la trasformazione di un insieme di canali graytone in canali retinati:
bool CREATE_DITHERING(CLASS_STR) { __cursor_wait // visualizza il cursore di waiting int n=_$str->img->GetChannelList()->Num(); DOC_AREA *ar=$obj->GetSize(); // usa le curve della variante attiva; // if (_$str->use_curve) { COLORWAY *cwy=_$str->img->GetColorwayProject()->GetColorway(); cwy->GetDensityCurves(_$str->_DENSITY); } _$str->threads= new CLB_thread_list; for (int i=0;i<n;i++) { __thread_new(THREAD_calc_dither); __thr_push_p(_$str->img->GetChannelList()->GetChannel(i)) // canale __thr_push_p(_$str->dth[i]) // dithering da usare __thr_push_p(&ar->dx) // dimensioni mappa generata __thr_push_p(&ar->dy) if (_$str->use_curve) __thr_push_p(_$str->_DENSITY[i]) // curva di correzione densità else __thr_push_p(0); _$str->threads->Add(_thr); } _$str->threads->UseDisplayProgressWindow(__translate("Create Screens channels"), __translate(" Wait: the program is building dithered channels..")); return _$str->threads->Start(); }
Prima osservazione: Come si può intuire dalla lettura del codice, ciascun canale graytone è retinatato da un thread; per fare questo si allocano tanti thread quanti sono i canali , aggiungendoli come thread figli ad un gestore della lista: CLB_thread_list :
_$str->threads= new CLB_thread_list;
CLB_thread_list ha il compito di generare un thread che rimane in attesa che tutti i thread figli siano correttamente terminati.
Il ciclo di for(..) che segue genera ed assegna i parametri a ciascun THREAD_calc_dither, aggiungendolo al CLB_thread_list
for (int i=0;i<n;i++) { __thread_new(THREAD_calc_dither); __thr_push_p(_$str->img->GetChannelList()->GetChannel(i)) // canale __thr_push_p(_$str->dth[i]) // dithering da usare __thr_push_p(&ar->dx) // dimensioni mappa generata __thr_push_p(&ar->dy) if (_$str->use_curve) __thr_push_p(_$str->_DENSITY[i]) // curva di correzione densità else __thr_push_p(0); _$str->threads->Add(_thr); }
Per farlo utilizza due macro di CLB_lang;
_thread_new :
genera un’istanza del tipo passato (THREAD_calc_dither). il nome dell’istanza è, convenzionalmente _thr per semplificare la scrittura del codice successivo.
__thr_push_p :
inserisce sequenzialmente i parametri utilizzati dalla classe per l’esecuzione dei calcoli. argomento della macro è sempre un puntatore; la macro è richiamata tante volte quanti sono gli argomenti da passare alla THREAD_calc_dither.
Quindi l’istanza _thread viene aggiunta alla lista di esecuzione.
.. _$str->threads->Add(_thr); ..
A terminazione del ciclo di costruzione, viene chiamata una funzione di CLB_thread_list che inizializza una finestra di visualizzazione dello stato di esecuzione dei thread; vengono assegnate due stringhe di visualizzazione
_$str->threads->UseDisplayProgressWindow(__translate("Create Screens channels"), __translate(" Wait: the program is building dithered channels.."));
La finestra (modale) verrà visualizzata durante l’esecuzione del/dei thread
return _$str->threads->Start();
Seconda osservazione: CLB_thread_list ::Start() manda in esecuzione tutti i thread figli chiamandone la funzione Start(), quindi lancia un thread interno di attesa terminazione.