scrittura delle funzioni thead
Un thread interno a CLB_thread è definito attraverso tre funzioni, definite usando le macro CLB_lang
- La prima funzione , che è di inizializzazione, è descritta fra le due macro __function_thread_init … __function_end.
- La seconda funzione , che è chiamata a conclusione del thread, è descritta fra le due macro __function_thread_close… __function_end.
- La terza funzione, il thread da eseguire, è scritta nel blocco __thread_function(calc_dither) … __thread_end.
I comandi macro nascondono tutta la complessità di controllo degli eventi connessa alla gestione del thread, quali le semaforizzazioni, la risposta egli eventi standard (sospensione, stop, continuazione) lanciati dall’esterno, o di attesa di eventi dall’interno (waiting di eventi esterni..). Sono uno strumento per standardizzare i controlli del processo gestito dal thread,
Il codice, relativo alla classe THREAD_calc_dither usata come esempio è sotto riportato.
/** funzione che prepara i dati del thread */ __function_thread_init __function_end /** disalloca i dati non più necessari allocati nel thread.. */ __function_thread_close __function_end __thread_function(calc_dither) __thr_pop_p(ichn,IMCHN) // canale da retinare __thr_pop_p(dth,DITHER) // modello di dithering usato __thr_pop_p(dx,int) // dimensioni mappa generata __thr_pop_p(dy,int) __thr_pop_p(_DENSITY,CURVE); // curva di correzione densità int siz=1; if (!IBF.OpenTempFile(*dx,*dy,siz)) EXIT_TASK DOC_AREA *sz=ichn->channel.GetSize(); // scala da applicare double scy=(double)sz->dy/(double)*dy; double scx=(double)sz->dx/(double)*dx; __$btm_Open((&(ichn->channel))) dth->StartFiltering(); UBYTE *src=new UBYTE[*dx]; // linea da scalare _$str->src = src; int npts = 0; // tabella densità calcolata if (_DENSITY) { _$str->_dens = _DENSITY->Get_points(&npts); if (npts != 101) { __trace(L"numero di punti curva densità sbagliato!") } } { for (int y = 0; y < *dy; y++) { if (!(y % 100)) { __test_thread_status __thr_sig_perc(((float)y*100.0f) / (float)*dy) } UBYTE *buf = BPIX(_BMAN, 0, (int)(scy*y)); // legge la linea sorgente UBYTE #define _DENS(v) _$str->_dens[v].Y // scalatura della linea originale; if (_DENSITY) { for (int x = 0; x < *dx; x++) src[x] = (UBYTE)(2.55f*(float)_DENS(buf[(int)(scx*x)])); } else for (int x = 0; x < *dx; x++) src[x] = (UBYTE)(2.55f*(float)buf[(int)(scx*x)]); UBYTE *map = IBF.GetLine(y, true); dth->Filter(map, src, *dx); } ichn->channel.FlushToFile(); // rilascia nuovamente il canale su file temporaneo __thr_sig_perc(100.0f); } __thread_end
Nel caso in discussione le funzioni di inizializzazione (__function_thread_init) e terminazione (__function_thread_close) , chiamate automaticamente dall’interno del thread attraverso i comandi CLB_lang, sono completamente vuote, non avendo compiti da svolgere ( ad esempio avrebbe potuto allocare o disallocare dati usati nel thread main..)
__thread_function( calc_dither ) { ….. } __thread_end
Il blocco definisce il thread di nome calc_thread eseguito mediante il comando __thread_exec(calc_dither) dalla funzione Start(). Si possono osservare un insieme di macro CLB_lang che svolgono diverse funzioni:
__thr_pop_p(ptr,type)
estrae uno dei parametri passati atttraverso la __thr_push_p in fase di allocazione del thread: ptr è il nome di un puntatore di tipo type inizializzato mediante questa macro.
La macro è richiamata più volte per ricevere, nell’ordine di immissione, tutti i parametri passati alla classe mediante CLB_thread::Set_argument_list(..) o la sequenza di comandi __thr_push_p.
Il corpo della funzione contiene le istruzioni necessarie al compito assegnato, ma non sono argomento di questo articolo. Vengono invece discusse alcune macro di CLB_lang usate nel ciclo di esecuzione: __test_thread_status e __thr_sig_perc.
__test_thread_status
è un comando macro estremamente importante, che ha il compito di analizzare lo stato del thread ed eventualmente compiere i passaggi associati. osserviamone la definizione:
#define __test_thread_status { __test_thread_paused __test_thread_exit }
la macro esegue in sequenza due altre macro __test_thread_paused e __test_thread_exit , così definite:
__test_thread_paused
#define __test_thread_paused if (__event_verified(EV_PAUSE_THR)) { __event_set( EV_PAUSEED_THR) __event_wait(EV_CONTINUE_THR); }
La macro verifica che sia verificato l’evento EV_PAUSE_THR (attivato da qualcuno all’esterno o all’interno del thread chiamando la funzione CLB_thread::Suspend() ) e, se si, assegna un flag ed attende che sia verificato l’evento EV_CONTINUE_THR (macro __event_wait(..)). fino a quel momento il thread rimane in uno stato di attesa. L’evento EV_CONTINUE_THR è alzato dall’esterno attraverso la CLB_thread::Continue() .
__thr_sig_perc(perc)
La macro segnala al gestore dei thread quale è la percentuale del ciclo attualmente eseguita. La segnalazione è usata da un thread (CLB_thread_list) per visualizzare lo stato del thread specifico, o dell’insieme dei thread figli. Se non è attivo un thread di cattura della percentuale, non succede niente.
notare che le macro __test_thread_status e __thr_sig_perc sono state inserite in un blocco che viene eseguito ogni cento linee analizzate: la scelta fatta dal programmatore è stata di non eseguire il test con frequenza eccessiva, considerando sufficiente quella scelta..
..... for (int y = 0; y < *dy; y++) { if (!(y % 100)) { __test_thread_status __thr_sig_perc(((float)y*100.0f) / (float)*dy) } ..... ..... .....
E’ importante osservare che non è obbligatorio inserire queste macro nel codice; Se non presenti, il thread don potrà essere interrotto dall’esterno (__test_thread_paused) o non fornirà informazioni sul suo stato di esecuzione (__thr_sig_perc)