Newsgroup: it.comp.programmare
Titolo: Impostazione progetto modello client/server con posix thread

Ciao a tutti,
Sto mettendo su un sistema client-server ed è la
prima volta che userò il multithreading in modo un
po' più sofisticato dei soliti esercizietti. Il mio
problema si può descrivere come segue.
Sulla generica connessione aperta, arrivano dei
pacchetti dati. Ciascuno di questi inizia con un
numero che identifica il particolare task a cui si
riferisce e a cui viene smistato (uso "task" come
"algoritmo da eseguire quando ci sono tutti i dati
necessari"). Esiste ovviamente anche un codice di
"fine dati" che segnala che i dati per quel task
sono finiti e quindi può partire la procedura di
esecuzione. Il fatto è che potrei avere N
connessioni aperte e M task, con M parecchio
maggiore di N e qui temo problemi visto che
l'algoritmo è in genere parecchio lungo.
Al momento, al top-level (nel main, visto che sono
in C++) partono 3 thread: uno per l'ascolto di
richiesta connessioni, uno per ricevere i pacchetti
dati, e l'altro per l'esecuzione. Ciascuno di questi
viene fermato con "sleep" per un certo tempo (al
momento 20 ms, che credo ragionevole e al momento
non pare sovraccaricare le CPU). La terza routine
non fa che scorrere l'elenco dei task e quando vede
che ce n'è uno pronto per l'esecuzione lo fa partire.
Ovviamente tutto questo funziona *ora* perché la
routine di esecuzione del task è vuota e quindi
viene eseguita molto più velocemente del clock
locale. Mi pare praticamente certo che con
l'implementazione effettiva del task succederà un
casino.
Pensavo che nella routine di esecuzione ogni task
potesse avere un suo thread, ma anzitutto potrebbero
essere molti e non è un buon approccio già per
questo, secondo poi sono un po' confuso coi livelli
di annidamento dei threads. Fin qui ho usato threads
nella stessa funzione ma non riesco a capire cosa
accadrebbe in casi più complicati di una funzione in
threading che ne attiva altre sempre in
multithreading. So che i posix thread non hanno
gerarchia ma forse per questo non so che pesci
prendere. Non so nemmeno dove collocare i vari
pthread_join eccetera.
Inoltre resta il problema che i task potrebbero
essere tanti e vedo inevitabile qualche sistema di
smistamento in coda e bilanciamento dei threads in
modo che siano attivi il massimo numero previsto
settato dall'utente. Non sono un programmatore e
vorrei mantenermi su qualcosa alla mia portata, si
può fare?
grazie!
Apx.
-- 
"La teoria è sapere come funzionano le cose e
non sapere come farle funzionare; la pratica è
sapere come far funzionare la cose senza sapere come
funzionano; il nostro scopo è unire la pratica
alla teoria per riuscire a non far funzionare
le cose senza capire come abbiamo fatto"

 può fare?
Tutto si puo' fare, ma se non sei un programmatore, mi chiedo perche'
ti metti a fare una cosa cosi' complessa invece di avvalerti di
librerie gia' pronte all'uso.

 librerie gia' pronte all'uso.
Magari ce ne fossero, ma ogni libreria vuole il suo
compilatore e ho perso sempre più tempo a tentare
porting invano che a riscrivere. Ormai è una specie
di riflesso pavloviano ma magari esistesse qualcosa.
Uso MinGW, che mi ha sollevato parecchi problemi
dandomene altri, anche se un po' meno dei
precedenti. Il problema restano documentazione ed
esempi. Non so valutare se proseguire o usare
librerie ammesso che ce ne siano. Per ora sono circa
200 linee e non ho l'impressione debbano diventare
tante di più, il grosso sta nell'algoritmo. Non ho
problemi di applicazioni real time o velocità, in
questo caso.
ciao e grazie per ora!


 questo caso.
L'algoritmo mi pare di avere capito che esuli dalla lettura dei thread.
Altrimenti vuol dire che non ho capito niente del tuo problema.
Scindiamo le cose: lettura da una parte ed elaborazione dall'altra.
Per quanto riguarda la lettura, sicuramente si puo' trovare qualcosa di 
pronto da cui prendere spunto, perche' e' un problema di tutti i giorni 
quello di leggere dei flussi di dati.

anzitutto grazie e scusa il cut selvaggio, ho
ovviamente letto tutto. Io potrei postare ma lo
trovo poco etico per il NG, so sempre qualche
centinaio di linee. Appena posso accedere al mio
disco remoto lo metto lì, altrimenti se le regole
non scritte del NG lo consentono, posto
direttamente. Qualcuno lo sa se si "può"?
A-ri-grazie infinite !
ciao


OK, fatto... l'ho messo sul mio disco remoto, e sta qua:
http://mio.discoremoto.alice.it/archeopteryx
sotto "client_server" ovviamente...
Due parole sull'approccio. Gravito attorno a uno
studio di ingegneria civile che ha una rete locale.
Spesso occorre scrivere pre e post processori,
intesi soprattutto come interfacce di conversione
tra formati (testo ASCII) di programma. Altre volte
ci sono compiti molto ripetitivi che hanno un po'
tutti, come verificare travi e pilastri. Ancora,
potrebbe trattarsi di elaborazioni di modelli con
migliaia di gradi di libertà e allora ci si mettono
anche ore. Fin qui ho scritto pletore di programmi
che non si sa su che PC stanno, quale versione sta
usando chi eccetera. Non solo, ma potrebbe capitare
di fare favori a studi amici e in certe circostanze
è scocciante sia dare anche solo un eseguibile che
dire "dammi tutto, faccio io e poi ti dò i
risultati". Se centralizzo tutto, in modo che tutto
stia sul mio PC e io tenga le redini della
situazione, penso di semplificarmi parecchio la
vita, e posso lasciare ad altri il compito di
mantenere i sorgenti dei client. Anzi, stabilito un
protocollo, chiunque ci si potrà interfacciare. Non
solo, ma collegando il tutto a un unico database si
possono coordinare meglio certi lavori collettivi.
Ancora, potrei dovermi occupare di programmazione
anche professionalmente, e per quanto non da grande
professionista e mettere su un modello client-server
è utile a imparare certi problemi.
Per tutto questo è chiaro che non ci sono problemi
di velocità o ottimizzazioni spinte. Il lavoro
svolto può essere "consegnato" anche minuti o ore
dopo - questo posso farlo mettendo in coda i lavori
finiti e spedirli quando una connessione viene
attivata e lo chiede.
Nel file .zip ci sono anche gli eseguibili anche se
sarebbe meglio ricompilare. La bozza attuale non è
operativa, non ha gestione errori e altro, il
protocollo è appena abbozzato. Il generico pacchetto
dati - al momento tramite console per provare -
inizia con due stringhe, una che identifica il
chiamante (al momento non fa verifica sulla
connessione e forse non è desiderabile) e la seconda
che identifica il compito da eseguire. Al momento,
dicevo, non si fa niente, solo ricevere e rispedire.
Il tutto è in C++ e chi ha MinGw e le librerie
posix_thread di cui esiste il porting, può
compilarlo direttamente a parte i necessari e credo
banali intervento sul makefile per la struttura
delle directories non locali. Come potete vedere non
aspiro a cose di livello eccelso e sofisticato,
basta che ci sia una base minima di chiarezza di
impostazione e che funzioni, le cose da ottimizzare
e curare saranno altre. Ogni commento e suggerimento
è benvenuto. Grazie!


 sotto "client_server" ovviamente...
Domani provero' a darci uno sguardo e vediamo...
Taglio tutto il tuo discorso e rispondo con un concetto:
le applicazioni client-server funzionano in modo che il server sta in 
ascolto, il client invia i dati e aspetta una risposta entro un certo 
tempo. Se la risposta non arriva entro quel tempo scatta il timeout.
Questo e' il concetto.
Normalmente il tempo di risposta e' nell'ordine dei secondi, come nel 
caso delle web application, ma nulla vieta che il "server" sia un umano 
e il canale di comunicazione sia la mail.
Il client invia una mail con i dati allegati, il server umano li prende, 
li elabora e manda una risposta coi risultati.
E' client-server pure questo, con un tempo di attesa molto piu' lungo.
Mi pare di avere capito che al momento tu faccia gia' da server umano 
per alcune realta' clienti tuoi, che ti mandano roba da elaborare.
Se il collo di bottiglia sta nella elaborazione, io farei una 
architettura di questo tipo:
- un canale di ascolto per ricevere i dati in ingresso.
- un processo di elaborazione.
- un canale di trasmissione in uscita per rimandarli indietro.
Una coda FIFO con i dati da elaborare.
Il processo di ascolto legge e mette in coda.
Il processo di elaborazione prende dalla coda ed elabora e manda al 
processo di trasmissione di ritorno.
E fin qui siamo nella pura e semplice teoria.
Domani, guardando i sorgenti, vediamo di entrare nel dettaglio.

 nel dettaglio.
grazie ancora.... ho letto la tua risposta,
ovviamente. Tieni solo presente che i tempi di
elaborazione possono variare di ordini di grandezza,
alcuni compiti richiedono manciate di secondi e una
risposta sul modello client-server potrebbe essere
indicata; ho già implementato, come si vede, una
rudimentale forma di timeout. Nulla vieta che i
risultati si mettano in una coda e una successiva
connessione permetta di riceverli, come dicevi, in
modo simile a una mail. Mi piacerebbe mantenere la
stessa struttura nel senso che sia una e non più di
una che coesistono o si switcha da una all'altra ma
ovviamente se ci sono soluzioni migliori le adotto.
a-ri-ciao!


 di annidamento dei threads. 
Non esistono livelli di annidamento, i thread sono percorsi di esecuzione
paralleli.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 capisco dove vada la sleep().
ho capito tutto ma non questo. Ho un loop
all'interno del quale c'è accept, che senza "Sleep"
mi impallava la CPU. Non sapevo come altro fare, ho
pensato che "accept" dovrà pur stare all'interno di
qualche ciclo che controlla se ci sono connessioni
in ingresso. Con certezza non ho capito come si usa
la funzione, e cercherò qualche esempio. Grazie per
questo e il resto!
ciao!


 mi impallava la CPU. 
Impossibile. La accept() � bloccante. Il loop va bene, ma la sleep non
serve: ogni qual volta chiami la accept() il sistema si ferma in attesa di
una connessione.


 di una connessione.
WOWO ottimo e grazie. A questo punto sicuramente era
un altro thread a impallare la CPU e ho attribuito
la cosa alla causa sbagliata, mo' levo l'istruzione
"Sleep".


 [...]
La soluzione migliore (IMHO, ovviamente) è quella di usare la 
programmazioni ad eventi per gestire le connessioni, ed un pool di thread 
o processi per gestire i task.
In pratica, quando una richiesta dal client è stata inviata, invii la 
richiesta del task al pool (se usi i thread conviene usare una queue 
thread safe).
Il task viene preso in consegna da uno dei thread/processi del pool, 
viene eseguito, e quando è stato completato notifica il thread principale 
(usando di nuovo una code thread safe, se usi i thread).
Nota che se usi i thread hai bisogno di un meccanismo di polling nel 
thread principale per verificare se un task è stato completato.
Alcuni framework hanno già il supporto completo (come Twisted Python).
Se usi i processi puoi usare una semplice pipe, registrando il file 
descriptor nel tuo event loop, per ricevere le notifiche.
Ti avverto che se fai tutto questo in C++ ti aspetta un grosso mal di 
testa.
Io ti consiglio Twisted Python, ma se non conosci già bene Python, ti 
richiede un 2-3 settimane di pratica.
Altrimenti potresti fare il passo più grosso ed usare Erlang, o altro 
linguaggio dedicato alla concorrenza.
Saluti   Manlio

o processi per gestire i task.
S�, anche questa soluzione va bene. Credo che alla fine molto dipenda dal
sistema operativo. Sotto Windows, per esempio, � un po' pi� oneroso
gestire i processi rispetto ai thread. Per le comunicazioni tra thread
principale e worker threads non ho mai usato la sincronizzazione ma sempre
le code dei messaggi di Windows, affidandomi quindi alla gestione
thread-safe da parte del s.o.


 thread-safe da parte del s.o.
Si, su Windows puoi passare l'handle del thread a WaitForMultipleObject.
Da un lato Windows ha un sistema più generico di quello POSIX per la 
gestione degli eventi, ma dall'altro lato ha limitazioni assurde.
Saluti  Manlio Perillo

 gestione degli eventi, ma dall'altro lato ha limitazioni assurde.
Quali limitazioni hai trovato?


 di thread) per client.
In effetti è quel che volevo, ma essenzialmente per
non dipendere troppo da Windows che non amo. I posix
thread mi paiono abbastanza semplici persino per me.
Molto della realizzabilità di un progetto dipende
anche dal mezzo. Certo, potrei wrappare alcune
funzioni ma non so cosa accadrebbe su altri sistemi.
ciao!


 non dipendere troppo da Windows che non amo.
La soluzione proposta da Manlio prescinde dal sistema operativo.
In ogni caso, secondo me e visto che mi � sembrato di capire che non sei
ferratissimo nel MT, per non trovarti a camminare in un campo minato, puoi
provare la soluzione multiprocesso. Probabilmente per te sarebbe pi�
semplice: hai un processo che gestisce le connessioni dai client e delega
l'elaborazione dei dati ai vari processi "worker". I processi secondari
possono comunicare col processo principale mediante semplici tecniche di
IPC (pipe, per esempio). Persino scalare, aggiungendo nuovi worker,
potrebbe essere pi� semplice.
