|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
[C++] Qt multithreading
Ho un quesito per quanto riguarda la programmazione con più di 2 thread....
se io ho 3 classi, una la struttura dati, le altre due devono accedervi spesso. Siccome non è possibile prevedere e conseguentemente sincronizzare gli accessi, utilizzo la classe QMutex per evitare accessi contemporanei.... La mia domanda è... questa classe la utilizzo dentro i metodi pubblici della classe per i dati? è corretto? Non mi interessa inserire QMutex dentro le altre due, ok? Quindi dentro ogni metodo per esempio un metodo "get" farei così: Codice:
QString charon::get_Host() { QString temp; Mutex.lock(); temp = Host; Mutex.unlock(); return(temp); } secondo voi? Grazie. Ultima modifica di Teo@Unix : 23-12-2009 alle 16:29. |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: Nov 2000
Città: MILANO
Messaggi: 2662
|
ad occhio e croce si. si sincronizza la classe che ha i dati cui si vuole accedere in modo esclusivo. quindi nei metodi della classe con i dati, che devono essere chiamati dalle altre due classi, si alza un mutex nella parte di codice chiamato critical race, che di solito è un membro - privato direi - della classe dati. quando uno arriva e trova il mutex lockato aspetta. occhio ai deadlock.
|
![]() |
![]() |
![]() |
#3 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Il valore di host viene mai cambiato ? Sui valori read-only non c'è bisogno di alcuna sincronizzazione.
La sincronizzazione c'è bisogno solamente in caso di read e write concorrenti o di write concorrenti su operazioni non atomiche. Per andare oltre dovresti studiarti qualche manuale sulla programmazione concorrente, perché esistono tanti, ma tanti trucchi per diminuire i conflitti sulle risorse. Ultima modifica di cionci : 24-12-2009 alle 09:26. |
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
In realtà, viene cambiato inizialmente, quando solo un thread è attivo, quindi si, dopo verrà solo letto.
In questo caso non dovrebbe essere necessaria la mutex. La mia idea relativamente al progetto "di prova" di cui avevo mostrato alcune parti di codice nell'altro post, era di associare un thread ad ogni porta da scansionare, in modo da aumentare di molto la velocità di esecuzione. Il programmino l'ho realizzato per ora solo con due thread. Quello che si occupa della scansione soffre dei timeout della connect(). Perciò avevo pensato di provare definendo 4 thread. Questi appena finiranno il lavoro con la loro porta andranno a prendersi la prossima e via dicendo fino all'ultima... per ora ho un pò di problemi con il buffer dei risultati .... ora ho escluso un problema di uso errato di mutex.... resta da vedere come gestire al meglio thread del genere.... Se questa domanda ha un senso, Come fareste idealmente per gestire più thread? edit: a ok, non avevo letto la tua modifica, si si comunque mi stò studiando la faccenda.... |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Non ha senso creare un thread per ogni porta da scansionare. Per diversi validi motivi:
- l'ip che scansioni molto probabilmente ti rileverà come una attacco DoS - l'efficienza, dopo un certo numero di thread, si va a perdere - il costo della creazione di un thread, seppur basso, è sempre un costo Quindi io ti consiglio un tot di thread, magari aumentabile tramite l'interfaccia grafica (numero di porte scansionate contemporaneamente). Che so, da 4-5 a non più di 10. Questo è detto "pool di thread". Questi thread non verranno creati e distrutti ad ogni scansione, ma verranno creati all'inizio e distrutti alla fine di tutte le scansioni. Dovrai fornire al pool di thread i dati su cui lavorare. Col sistema dei segnali-slot è veramente semplice avere la notifica del completamente del lavoro, non c'è nemmeno bisogno di lavorare troppo di mutex o gestire la concorrenza sui dati. Quando un thread avrà completato il suo lavoro emetterà un segnale (magari fornendo l'indice del thread che ha finito il lavoro o il suo puntatore) e si metterà in attesa su un QSemaphore. Questo segnale sarà collegato ad uno slot che avrà il compito di: - raccogliere il risultato - fornire al thread un nuova porta su cui andare a lavorare - riavviare il thread andando ad agire sul semaforo Nel subclassing di QThread potresti aggiungere un metodo connect(const QString &IP, const int port) che fa le ultime due cose. Quando crei e avvii il thread potrebbe automaticamente andarsi bloccare sul QSemaphore in attesa che il chiamante vada a fornirgli la porta su cui lavorare con connect (basta inizializzare il QSemaphore a 0). Ultima modifica di cionci : 24-12-2009 alle 09:57. |
![]() |
![]() |
![]() |
#6 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
![]() dunque un semaforo mi fungerebbe da parcheggio... perfetto avevo tentato di fare questa parte creandomi una routine nella classe MainWindow che gestiva proprio 4 thread... così mi sembra semplice. Provo. |
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Causa i consueti riti Natalizi non ho avuto molto tempo per provare,
ho però letto un pò la guida e ho visto che il "pool di threads" è una classe vera e propria, ho letto anche che, se il mio inglese non mi tradisce, occorre reimplementare QRunnable, nel senso che bisogna creare una classe derivata da QRunnable? Quel che ho fatto è: Nella classe MainWindow, dichiarata una classe derivata da QThreadPool. Devo ora crearmi anche una classe QRunnable? E poi dichiarare in QThreadPool i miei thread QRunnable? In questa metterei la connect() ecc... spero di essermi spiegato bene... Grazie. Ultima modifica di Teo@Unix : 25-12-2009 alle 22:15. |
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Ma non importa. Non sapevo nemmeno che esistesse
![]() Per i tuoi scopi va più che bene un approccio più semplice. Almeno entri meglio nell'ottica dei thread e della loro gestione. Con QThreadPool, da quanto leggo, tutto il lavoro sui thread viene nascosto. QThreadPool può essere comoda quando ci sono molti compiti diversi che possono essere svolti dai thread. |
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Già, siccome nel mio programmino di prova il compito è sempre le stesso, niente QThreadPool....
A questo punto ciò che farei è: 1 una classe che contiene il metodo per la scansione [scanthread] 2 la solita classe MainWindow (mantegno quella della prima versione del programma) [MainWindow] 3 la classe struttura dati (anche questa è la medesima) [charon] Ora, nel costruttore di MainWindow creo 4 elem. scanthread. Quindi mi hai parlato di QSemaphore per gestirli... però non saprei bene, anche dagli esempi proposti non ho trovato nulla di simile..... QSemaphore mi sembra molto simile a QMutex.... ![]() Alla run() dei thread potrei anche fargli ritornare il puntatore del thread stesso... ma non ho capito bene come utilizzarlo dopo per controllare i threads.... Thanks. |
![]() |
![]() |
![]() |
#10 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Usando segnali e slot non hai bisogno della classe per condividere i dati. Infatti puoi recuperare i dati ed assegnarne di nuovi su cui lavorare direttamente della classe del tuo thread, perché il thread è fermo sul semaforo, quindi non ci sarai mai concorrenza.
|
![]() |
![]() |
![]() |
#11 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
|
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Hai scritto tu "dunque un semaforo mi fungerebbe da parcheggio...", quindi credevo che avessi capito che l'esecuzione di un acquire su un semaforo rosso implica il blocco del thread. Il thread va risvegliato solo dopo che avrai inserito in nuovi dati su cui dovrà andare a lavorare eseguendo una release.
|
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
forse ho capito,
la mia run del thread è corretto abbia questo prototipo: ScanThread* ScanThread::run(QHostAddress host,unsigned int port,short timeout) |
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Non si era detto che appena eseguivi la run il thread si andava a bloccare sul semaforo ?
I dati su cui lavorare glieli puoi fornire dopo, con un metodo della classe del thread (che andrà anche a risvegliarlo). Creati il pool di thread in modo che siano tutti bloccati in attesa di riceve le informazioni per partire. |
![]() |
![]() |
![]() |
#15 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
...
![]() dovrò passargli il semaforo però... umh? ScanThread* run(QSemaphore*); cioè dovrò per forza di cose passare il puntatore del semaforo al costruttore del thread. giusto? Nel momento che il thread avrà finito farà sem.acquire() bloccandosi finche in mainwindow non eseguo sem.release().... ma posso utilizzare i metodi del thread per passargli i dati se esso è bloccato sul semaforo.... ..scusa, ma stò faticando con questa parte.... ![]() |
![]() |
![]() |
![]() |
#16 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Non importa, il semaphoro lo puoi creare come variabile membro del thread.
Poi dopo andrai a chiamare, ad esempio: thread->connect(......): In cui passerai i dati su cui il thread andrà a lavorare. In fondo alla conncect chiamerai la release sul semaforo. |
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Potrebbe essere una valida implementazione dell classe thread?
Ti sembra giusto l'uso dei semafori? (non ho provato nulla) ![]() Codice:
#ifndef SCANTHREAD_H #define SCANTHREAD_H #include <QThread> #include <QTcpSocket> #include <QHostAddress> #include <QString> #include <QSemaphore> class ScanThread : public QThread { Q_OBJECT private: //Dati unsigned int ThisPort; short int ThisTimeout; QString ThisHost; QTcpSocket socket; QString buffer; QSemaphore semaphore; //Metodi void ResolveService(unsigned int); public: //Metodi pubblici ScanThread(); void run(); QString get_buffer(); public slots: void AwakeningProcess(unsigned int,QString,short); }; #endif // SCANTHREAD_H Codice:
#include "scanthread.h" ScanThread::ScanThread() { semaphore(0); } void ScanThread::run() { while(semaphore.acquire(1)) { socket.connectToHost(ThisHost,Thisport); if(socket.waitForConnected(ThisTimeout*1000)) { buffer.append(tr("Rilevata porta %1 aperta - ").arg(ThisPort)); ResolveService(port); } } } void ScanThread::ResolveService(unsigned int port) { struct servent* service; if((service = getservbyport(htons(port),"tcp")) == NULL) buffer.append(tr("Impossibile risalire al servizio in esecuzione sulla porta\n")); else buffer.append(tr("%1 in esecuzione\n").arg(service->s_name)); } void ScanThread::AwakeningProcess(unsigned int port,QString Host,short timeout) { ThisPort = port; ThisTimeout = timeout; ThisHost = Host; semaphore.release(1); } Ultima modifica di Teo@Unix : 26-12-2009 alle 17:01. |
![]() |
![]() |
![]() |
#18 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Va benissimo
![]() Ottima scelta del while per il ciclo, ma acquire non torna alcun valore, è void. Quindi dovresti mettere la acquire all'interno del ciclo e fare un while(1). L'unico problema è questo: Codice:
ScanThread::ScanThread() { semaphore(0); } ![]() Ultima modifica di cionci : 26-12-2009 alle 17:21. |
![]() |
![]() |
![]() |
#19 |
Senior Member
Iscritto dal: Mar 2009
Messaggi: 753
|
Ok, ho modificato opportunamente.
Ora, questione gestione dei threads......... ![]() Lo faccio nella classe mainwindow naturalmente.... siccome non posso piantarci un ciclo che crei attese se nò sarei punto capo...... avevo pensato di avviarne inizialmente uno, praticamente nello slot associato al pulsante start metterei: thread0->AwakeningProcess(CurPort,Host,timeout); va a chiamare il metodo del mio thread e fa eseguire al thread un nuovo ciclo con i nuovi dati..... se ci sono più di una porta obiettivo avvio gli altri... il tutto sarebbe così: (anche se non mi pare molto elegante) Codice:
CurPort = Sport; thread0->AwakeningProcess(CurPort,Host,timeout); if(CurPort==Eport) {}//Ho finito... CurPort++; thread1->AwakeningProcess(CurPort,Host,timeout); if(CurPort==Eport) {}//Ho finito... CurPort++; thread2->AwakeningProcess(CurPort,Host,timeout); if(CurPort==Eport) {}//Ho finito... CurPort++; thread3->AwakeningProcess(CurPort,Host,timeout); Nel caso le porte sono più di 4.... avevo pensato di collegare il segnale finished() per ogni thread con la funzione: Codice:
void MainWindow::NextThread(ScanThread* thread) { ScanThread* CurrentThread = thread; CurrentThread->AwakeningProcess(++CurPort,Host,timeout); } IL problema che ho incontrato è che nella funzione connect() non posso pasare un argomento allo slot, vuole solo i tipi.... Codice:
connect(thread0,SIGNAL(sleep()),this,SLOT(NextThread(ScanThread*))) Hai idee migliori per gestire i threads? Grazie molte. Ultima modifica di Teo@Unix : 26-12-2009 alle 18:21. |
![]() |
![]() |
![]() |
#20 |
Senior Member
Iscritto dal: Apr 2000
Città: Vicino a Montecatini(Pistoia) Moto:Kawasaki Ninja ZX-9R Scudetti: 29
Messaggi: 53971
|
Codice:
void MainWindow::NextThread(ScanThread* thread) { ScanThread* CurrentThread = thread; //qui devi anche recuperare il risultato CurrentThread->AwakeningProcess(++CurPort,Host,timeout); } ![]() http://doc.trolltech.com/4.5/signals...-small-example Crea un signal nella classe del thread che si chiama ad esempio workDone(ScanThread* thread). Lo devi collegare allo slot NextThread tramite la connect. Dopo che hai emesso il segnale devi fare la acquire sul semaforo. |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 04:13.