PDA

View Full Version : [C++] Qt multithreading


Teo@Unix
23-12-2009, 16:27
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ì:
QString charon::get_Host()
{
QString temp;
Mutex.lock();
temp = Host;
Mutex.unlock();
return(temp);
}

dove "Host" è un elemento della struttura dati.
secondo voi?
Grazie.

Black imp
24-12-2009, 01:55
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.

cionci
24-12-2009, 09:15
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.

Teo@Unix
24-12-2009, 09:28
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....

cionci
24-12-2009, 09:49
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 (http://doc.trolltech.com/4.5/signalsandslots.html#a-small-example) (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).

Teo@Unix
24-12-2009, 10:12
:D grazie chiarissimo,
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.

Teo@Unix
25-12-2009, 22:10
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.

cionci
26-12-2009, 10:27
Ma non importa. Non sapevo nemmeno che esistesse :D
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.

Teo@Unix
26-12-2009, 10:53
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....:rolleyes:

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.

cionci
26-12-2009, 11:02
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.

Teo@Unix
26-12-2009, 11:08
perché il thread è fermo sul semaforo, quindi non ci sarai mai concorrenza.

sono un pò disorientato riguardo a questo punto però, cosa intendi per "fermo sul semaforo"?

cionci
26-12-2009, 11:20
sono un pò disorientato riguardo a questo punto però, cosa intendi per "fermo sul semaforo"?
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.

Teo@Unix
26-12-2009, 11:33
forse ho capito,
la mia run del thread è corretto abbia questo prototipo:
ScanThread* ScanThread::run(QHostAddress host,unsigned int port,short timeout)

cionci
26-12-2009, 11:36
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.

Teo@Unix
26-12-2009, 13:12
...:D

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....:(

cionci
26-12-2009, 14:08
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.

Teo@Unix
26-12-2009, 16:59
Potrebbe essere una valida implementazione dell classe thread?
Ti sembra giusto l'uso dei semafori? (non ho provato nulla):fagiano:

#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


#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);
}

l'ultima funzione qui sopra dovrebbe passargli i parametri e svegliare il thread... mmmm che ne pensi?

cionci
26-12-2009, 17:19
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:

ScanThread::ScanThread()
{
semaphore(0);
}

Non può funzionare così, devi utilizzare una lista di inizializzazione ;) In quanto in quel punto lì, semaphore è già stato inizializzato. Comunque in questo caso non serve perché il QSemaphore viene già inizializzato a zero.

Teo@Unix
26-12-2009, 17:59
Ok, ho modificato opportunamente.

Ora, questione gestione dei threads.........:stordita:

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)

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:
void MainWindow::NextThread(ScanThread* thread)
{
ScanThread* CurrentThread = thread;
CurrentThread->AwakeningProcess(++CurPort,Host,timeout);
}

che incrementa la porta corrente e riavvia il thread per una nuova scansione, in questo modo i thread dovrebbeo essere sempre impegnati.
IL problema che ho incontrato è che nella funzione connect() non posso pasare un argomento allo slot, vuole solo i tipi....
connect(thread0,SIGNAL(sleep()),this,SLOT(NextThread(ScanThread*))) quindi come passo il puntatore del thread disponibile?

Hai idee migliori per gestire i threads?

Grazie molte.

cionci
27-12-2009, 09:22
void MainWindow::NextThread(ScanThread* thread)
{
ScanThread* CurrentThread = thread;
//qui devi anche recuperare il risultato
CurrentThread->AwakeningProcess(++CurPort,Host,timeout);
}
Inoltre devi emettere un segnale per risolvere il problema ;)
http://doc.trolltech.com/4.5/signalsandslots.html#a-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.

Teo@Unix
27-12-2009, 09:47
Se non ho capito male dall'esempio che mi hai linkato, nella connect() si dovranno mettere segnale e slot con lo stesso argomento....
io ragionavo sul valore di ritorno... non so se è giusto ma verrebbe:

connect(thread0,SIGNAL(WorkDone(ScanThread*)),this,SLOT(NextThread(ScanThread*)));

come fa il segnale a sapere che l'argomento è l'indirizzo di se stesso, cioè a me verrebbe spontaneo mettere this lì.... mmmm :confused:

edit: a no! scusa, scusa ho capito...
poi farò this->WorkDone(this);

um?

cionci
27-12-2009, 09:49
this lo passi nella emit ;)
Ovviamente dovrai connettere tutti i signal di tutti i thread a NextThread.

Teo@Unix
27-12-2009, 11:36
Ok, sto cercando di compilare mi manca solo di pensare come gestire l'evento del click del pulsante STOP....

ho un problema sulla compilazione che proprio non capisco :confused: ... innanzi tutto mi da dei warning :

overriding commands for target `debug/moc_scanthread.cpp'
ignoring old commands for target `debug/moc_scanthread.cpp'
[...]

è un'ora che cerco il problema ma proprio non lo trovo... bah!
e in più anche questi inspiegabili messaggi di definizioni multiple:

c:/Qt/2009.05/qt/include/QtCore/../../src/corelib/tools/qchar.h:58: multiple definition of `ScanThread::ScanThread()'
debug/scanthread.o:c:/Qt/2009.05/qt/include/QtCore/../../src/corelib/tools/qchar.h:58: first defined here
[....]

posso anche postare il codice nel caso... ma non mi sembra di aver commesso errori di include o quant'altro..... :mc:

cionci
27-12-2009, 11:41
Mettimi tutto il progetto da qualche parte ;)

Teo@Unix
27-12-2009, 14:01
grazie,

da Qui (ftp://95.237.206.126/) puoi scaricarti il progetto, Entra come "unanymous" la password non c'è.

cionci
27-12-2009, 14:28
Prova ad eliminare queste righe dal file .pro:

# -------------------------------------------------
# Project created by QtCreator 2009-12-20T22:40:21
# -------------------------------------------------
QT += network
TARGET = HostScanner
TEMPLATE = app
SOURCES += main.cpp \
mainwindow.cpp \
scanthread.cpp \ <-----------------
scanthread.cpp
HEADERS += mainwindow.h \
scanthread.h \ <----------------
scanthread.h
FORMS += mainwindow.ui

# Per la compilazione su Linux commentare la riga seguente
LIBS += C:\Qt\2009.05\mingw\lib\libws2_32.a

Teo@Unix
27-12-2009, 14:42
Ci hai preso in pieno....:)
non capisco come mai erano elencati 2 volte quei file, probabilmente ho commesso qualche errore senza accorgermi, ed il file pro non l'ho guardato perbacco! Grazie, ora fila.

Mi manca da pensare a come interrompere la scansione se viene premuto stop.
Poi farò un pò di debug, vediamo che concludo.:)
Thank you.

Teo@Unix
28-12-2009, 12:41
:cool: infine ieri sera ho concluso ed il programma funziona bene guadagnando in velocità.
Innanzi tutto ti ringrazio per il supporto.

Ora sono impegnato ma posterò per completezza e anche per chi avesse seguito la discussione, il codice della classe MainWindow dove vengono gestiti i thread.

Il programma seppur magari particolare rimane semplice, gestire più thread complessi potrebbe richiedere a mio avviso una gestione migliore di quella che ho fatto.
Di conseguenza rimango tutt'orecchie per consigli riguardo la gestione adottata.

P.S.
Programmando in multi-threading con, come ho fatto io, 4 thread si può quindi dire che la CPU quad-core tenderà ad ossociare un thread per CPU?
(trascurando naturalmente i vari fattori che influenzano l'attività della CPU)
E quindi nel caso il programma facesse operazioni pesanti, la CPU sarebbe sfruttata al 100%? Invece che al 25%?

cionci
28-12-2009, 14:04
Sul PS: sì ;)

Ti suggerisco, anche a fini didattici, di rendere variabile il numero di thread nel pool, permettendone l'aumento o la diminuzione a piacere. Metti tutti i vari thread in un vector.

Teo@Unix
28-12-2009, 22:41
Ti suggerisco, anche a fini didattici, di rendere variabile il numero di thread nel pool, permettendone l'aumento o la diminuzione a piacere.

Si, ho pensato anche io che sarebbe stato utile.
Difatti ho inserito la possibilità di abilitare il mutlithreading e, una volta fatto selezionare il numero di questi.

Ho però fatto una "gestione manuale". Credo sia migliorabile, ad esempio sicuramente utilizzando come hai suggerito la classe QVector.

Vedrò nei prossimi programmi di fare una cosa migliore.

Intanto come detto, sottopongo a giudizio il codice della classe MainWindow commentato il più possibile:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMessageBox>
#include <QHostInfo>
#include "scanthread.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0); //Costruttore
~MainWindow(); //Distruttore
enum Mode { ScanMode, StanbyMode, ClosingMode };

protected:
void changeEvent(QEvent *e);

private:
Ui::MainWindow *ui;
unsigned int Sport, CurPort, Eport; // Range porte
short int timeout; // Timeout
QString Host; // Host obiettivo
short ScanCompleteFlag, StopWasPressed,NumberOfThreads; // Flags
bool MultiThreadsEnabled; // Abilita / Disabilita multithreading
QList<QString> numbthreads; // Il numero dei threads configurabili
// Threads
ScanThread* thread0;
ScanThread* thread1;
ScanThread* thread2;
ScanThread* thread3;
// Funzioni
void SetInterface(Mode);
void ScanComplete();

private slots:
void InitScanner(); // Prepara i dati e avvia il thread
void NextThread(ScanThread*); // Gestisce il thread e stabilisce se deve essere riavviato con altri dati
void StopByUser(); // Il tasto "Stop" è stato premuto
void EnableMultithread(bool); // Rende attiva la ComboBox per la selezione del numero di threads
};

#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
// Preparo lista per il numero di thread possibili
numbthreads.append(tr("Due - X2"));
numbthreads.append(tr("Tre - X3"));
numbthreads.append(tr("Quattro - X4"));
// Ciò che mi serve per l'opzione multithreading
ui->ThreadsNumbercomboBox->addItems(numbthreads);
MultiThreadsEnabled = false;
// Connetto pulsanti e slot
connect(ui->startpushButton,SIGNAL(clicked()),this,SLOT(InitScanner()));
connect(ui->stoppushButton,SIGNAL(clicked()),this,SLOT(StopByUser()));
connect(ui->EnableMultithreadcheckBox,SIGNAL(clicked(bool)),this,SLOT(EnableMultithread(bool)));
// Il mio pool di thread :D
thread0 = new ScanThread;
thread1 = new ScanThread;
thread2 = new ScanThread;
thread3 = new ScanThread;
thread0->start();
thread1->start();
thread2->start();
thread3->start();
// Setto l'interfaccia
SetInterface(StanbyMode);
ui->progressBar->setMinimum(10);
ui->progressBar->setMaximum(10);
ui->ThreadsNumbercomboBox->setEnabled(false);
// Gestione dei threads
connect(thread0,SIGNAL(WorkDone(ScanThread*)),this,SLOT(NextThread(ScanThread*)));
connect(thread1,SIGNAL(WorkDone(ScanThread*)),this,SLOT(NextThread(ScanThread*)));
connect(thread2,SIGNAL(WorkDone(ScanThread*)),this,SLOT(NextThread(ScanThread*)));
connect(thread3,SIGNAL(WorkDone(ScanThread*)),this,SLOT(NextThread(ScanThread*)));
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::changeEvent(QEvent *e)
{
QMainWindow::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}

void MainWindow::SetInterface(Mode currentmode)
{
switch(currentmode)
{
case ScanMode:
{
ui->ThreadsNumbercomboBox->setEnabled(false);
ui->EnableMultithreadcheckBox->setEnabled(false);
ui->startpushButton->setEnabled(false);
ui->stoppushButton->setEnabled(true);
ui->hostlineEdit->setEnabled(false);
ui->sportspinBox->setEnabled(false);
ui->eportspinBox->setEnabled(false);
ui->timeoutspinBox->setEnabled(false);
ui->captionlabel->setText(tr("Scansione di \"%1\" in corso... attendere ...").arg(Host));
break;
}
case StanbyMode:
{
if(MultiThreadsEnabled)
ui->ThreadsNumbercomboBox->setEnabled(true);
else
ui->ThreadsNumbercomboBox->setEnabled(false);
ui->EnableMultithreadcheckBox->setEnabled(true);
ui->startpushButton->setEnabled(true);
ui->stoppushButton->setEnabled(false);
ui->hostlineEdit->setEnabled(true);
ui->timeoutspinBox->setEnabled(true);
ui->sportspinBox->setEnabled(true);
ui->eportspinBox->setEnabled(true);
ui->captionlabel->clear();
break;
}
case ClosingMode:
{
ui->captionlabel->setText(tr("Attendere, concludo le operazioni in corso ....."));
ui->stoppushButton->setEnabled(false);
break;
}
}
}

void MainWindow::InitScanner()
{
//Controllo del campo host name
if(ui->hostlineEdit->text().isEmpty())
{
QMessageBox::information(this,tr("Campo host vuoto"),tr("Devi specificare un indirizzo o un nome FQDN."));
return;
}
//Controllo che il range delle porte sia valido
if(ui->sportspinBox->value() > ui->eportspinBox->value())
{
QMessageBox::information(this,tr("Range di porte non valido"),tr("Il range di porte dichiarato non può essere negativo"));
return;
}
// Risolvo nome host
QHostAddress ipv4addr;
QHostInfo address = QHostInfo::fromName(ui->hostlineEdit->text());
if(!address.addresses().isEmpty())
ipv4addr.setAddress(address.addresses().first().toIPv4Address());
else
ipv4addr = ui->hostlineEdit->text();
if(ipv4addr.isNull())
{
QMessageBox::information(this,tr("Nome host o indirizzo non valido"),tr("Il nome host o l'indirizzo IP immesso non è valido"));
return;
}
/* Prendo dati dall'interfaccia */
Sport = ui->sportspinBox->value();
Eport = ui->eportspinBox->value();
timeout = ui->timeoutspinBox->value();
Host = ipv4addr.toString();
MultiThreadsEnabled = ui->EnableMultithreadcheckBox->isChecked();
if(MultiThreadsEnabled)
NumberOfThreads = (ui->ThreadsNumbercomboBox->currentIndex()+2);
else
NumberOfThreads = 1;
// Controllo validità numero di thread rispetto alle porte
if(MultiThreadsEnabled)
{
if(Eport - Sport == 0)
{
QMessageBox::information(this,tr("Numero di thread non valido"),tr("Selezionati più thread che porte"));
return;
}
if(Eport - Sport == 1)
{
if(NumberOfThreads > 2)
{
QMessageBox::information(this,tr("Numero di thread non valido"),tr("Selezionati più thread che porte"));
return;
}
}
if(Eport - Sport == 2)
{
if(NumberOfThreads > 3)
{
QMessageBox::information(this,tr("Numero di thread non valido"),tr("Selezionati più thread che porte"));
return;
}
}
}
/* Setto interfaccia */
ui->outplainTextEdit->clear();
SetInterface(ScanMode);
/* Setto ProgressBar - min */
ui->progressBar->setMinimum(Sport-1);
ui->progressBar->setMaximum(Eport+1);
ui->progressBar->setValue(Sport);
/* Avvio [ Multithreading a 4 thread ] */
ScanCompleteFlag = 0;
StopWasPressed = 0;
CurPort = Sport;
// Risveglio il primo thread e gli passo i dati su cui lavorare
thread0->AwakeningProcess(CurPort,Host,timeout);
if(MultiThreadsEnabled)
{
if(!(NumberOfThreads >= 2)) return;
// Se il range di porte è > 1
if(CurPort==Eport) {ScanComplete(); return;}
CurPort++;
// Risveglio il secondo thread e gli passo i dati su cui lavorare
thread1->AwakeningProcess(CurPort,Host,timeout);
if(!(NumberOfThreads >= 3)) return;
// Se il range di porte è > 2
if(CurPort==Eport) {ScanComplete(); return;}
CurPort++;
// Risveglio il terzo thread e gli passo i dati su cui lavorare
thread2->AwakeningProcess(CurPort,Host,timeout);
if(!(NumberOfThreads >= 4)) return;
// Se il range di porte è > 3
if(CurPort==Eport) {ScanComplete(); return;}
CurPort++;
// Risveglio il quarto thread e gli passo i dati su cui lavorare
thread3->AwakeningProcess(CurPort,Host,timeout);
// Se il range è > 4 il primo thread che conclude verrà rimesso al lavoro con la porta successiva
// e così via gli altri fino alla porta finale.
}
}



void MainWindow::NextThread(ScanThread* thread)
{
ui->outplainTextEdit->setPlainText(ui->outplainTextEdit->toPlainText().append(thread->get_buffer()));
// Controllo se ci sono ancora porte da controllare
if(CurPort < Eport)
{
thread->AwakeningProcess(++CurPort,Host,timeout);
ui->progressBar->setValue(CurPort);
return;
}
// Controllo se era l'ultimo thread ad aver concluso
if(ScanCompleteFlag < NumberOfThreads-1)
{
// Se non lo era prendo nota che un'altro thread ha concluso il lavoro
ScanCompleteFlag++;
}
else // Altrimenti significa che era l'ultimo thread
ScanComplete();// Posso passare il controllo all'interfaccia
}

void MainWindow::ScanComplete()
{
// Posiziono l'indicatore di avanzamento alla fine
ui->progressBar->setValue(Eport+1);
// Abilito l'interfaccia
SetInterface(StanbyMode);
if(StopWasPressed)
ui->outplainTextEdit->setPlainText(ui->outplainTextEdit->toPlainText().append(tr("\n--> Scansione interrotta dall'utente <---\n")));
else
ui->outplainTextEdit->setPlainText(ui->outplainTextEdit->toPlainText().append(tr("\n--> SCANSIONE COMPLETATA <---\n")));
}

void MainWindow::StopByUser()
// Il pulsante stop è stato premuto dall'utente, fine dei giochi... :(
{
CurPort = Eport+1;// Aspetto la fine delle operazioni in corso per chiamare il prima possibile ScanComplete()
SetInterface(ClosingMode);
StopWasPressed = 1;
}

void MainWindow::EnableMultithread(bool state)
// Non servono commenti direi...
{
switch(state)
{
case true:
{
ui->ThreadsNumbercomboBox->setEnabled(true);
break;
}
case false:
{
ui->ThreadsNumbercomboBox->setEnabled(false);
break;
}
}
}

la classe del thread non la posto altrimenti allungo troppo, è comunque molto simile alla versione già postata in precedenza.

In conclusione penso comunque che il risultato ottenuto sia apprezzabile, visto che sono a poco alle prese con le UI grafiche.

cionci
29-12-2009, 07:51
Metti i thread in un vector. Vedrai che capire come poterli aumentare o diminuire dinamicamente ;)

Teo@Unix
29-12-2009, 08:06
Purtroppo anche oggi ho l'acqua alla gola. :D
Ma proverò senz'altro.