2012-01-23 11 views
13

Sono novizio di Qt e sto cercando multi-threading in Qt.
Come ho imparato a Qt Documents, ho definito due classi per due fili:Come utilizzare QMutex?

#include <QThread> 
#include <QMutex> 

class thread_a : public QThread 
{ 
    Q_OBJECT 
public: 
    explicit thread_a(QObject *parent = 0); 
    int counter; 

protected: 
    void run(); 
}; 

E in CPP di file:

#include "thread_a.h" 

thread_a::thread_a(QObject *parent) : 
    QThread(parent) 
{ 
    counter=0; 
} 

void thread_a::run() 
{ 
    counter++; 
} 

Seconda classe thread è lo stesso, ma con counter-- in run() metodo.
Quindi eseguo questi due thread da main.ccp.

Ora la mia domanda:
Come posso condividere counter in thread_a e thread_b utilizzando QMutex?

+2

** In questo caso non è necessario un mutex. ** Sembra che ci siano 2 classi separate per ogni thread, come suggerito da 'class thread_a' dichiarazione. Ciò significa che quando entrambe le classi sono istanziate, ciascuna avrà la propria copia di 'counter int;' e la diminuzione del 'counter' di un oggetto non influenzerà il' counter' dell'altra. – karlphillip

+1

Grazie a @karlphillip, hai ragione. Ma se voglio condividere 'counter' con' thread_a' e 'thread_b' che cosa suggerisci (usando' QMutex' solo per scopi didattici) –

+0

possibile duplicato di [Cercando di avere problemi con QThread] (http://stackoverflow.com)/questions/6084110/try-to-get-problems-with-qthread) – karlphillip

risposta

25

Invece di avere i dati all'interno di un thread, spostare i dati all'esterno del thread, proteggerlo e quindi accedervi da entrambi i thread.

Quello che segue è uno schizzo, di che cosa si potrebbe fare:

class Counter 
{ 
    public: 
    Counter():mMutex(),mCounter(0){} 
    int inc() 
    { 
     QMutexLocker ml(&mMutex); 
     return mCounter++; 
    } 
    int dec() 
     QMutexLocker ml(&mMutex); 
     return mCounter--; 
    } 
    private: 
    QMutex mMutex; 
    int mCounter; 
    Q_DISABLE_COPY(Counter) 
}; 

class ThreadA : public QThread 
{ 
    public: 
    ThreadA(Counter* ctr); 
    /* ... */ 
}; 

class ThreadB : public QThread 
{ 
    public: 
    ThreadB(Counter* ctr); 
    /* ... */ 
}; 

Il costrutto di Counter è spesso definito come un Monitor, da Wikipedia (sottolineatura mia):

In concomitante programmazione, un monitor è un oggetto o modulo destinato ad essere utilizzato in modo sicuro da più di un thread. La caratteristica che definisce un monitor è che i suoi metodi sono eseguiti con mutua esclusione. Cioè, in ogni momento, al massimo un thread può eseguire uno qualsiasi dei suoi metodi. Questa esclusione reciproca semplifica enormemente il ragionamento sull'implementazione dei monitor rispetto al ragionamento sul codice parallelo che aggiorna una struttura dati.

In questo caso specifico, un costrutto più efficiente sarebbe QAtomicInt. Questo guadagna atomicità dall'uso di speciali istruzioni della CPU. Questa è una classe di basso livello che potrebbe essere utilizzata per implementare altri costrutti di threading.


Edit - Esempio completo

Utilizzando correttamente le discussioni con lo stato condiviso non è banale. Si consiglia di prendere in considerazione l'utilizzo di segnali/slot Qt con connessioni in coda o altri sistemi basati su messaggi.

In alternativa, altri linguaggi di programmazione come Ada supportano Thread e Monitor (oggetti protetti) come costrutti nativi.

Ecco un esempio operativo completo. Questo è solo un codice di esempio, non utilizzare QTest::qSleep in codice reale.

objs.h

#ifndef OBJS_H 
#define OBJS_H 

#include <QtCore> 

class Counter 
{ 
    public: 
     Counter(int init); 
     int add(int v); 
    private: 
     QMutex mMutex; 
     int mCounter; 
     Q_DISABLE_COPY(Counter) 
}; 

class CtrThread : public QThread 
{ 
    Q_OBJECT 
    public: 
     CtrThread(Counter& c, int v); 
     void stop(); 
    protected: 
     virtual void run(); 
    private: 
     bool keeprunning(); 
     Counter& mCtr; 
     int mValue; 
     bool mStop; 
     QMutex mMutex; 
}; 

#endif 

objs.cpp

#include "objs.h" 

Counter::Counter(int i): 
    mMutex(), 
    mCounter(i) 
{} 

int Counter::add(int v) 
{ 
    QMutexLocker ml(&mMutex); 
    return mCounter += v; 
} 

/////////////////////////////////////// 

CtrThread::CtrThread(Counter& c, int v): 
    mCtr(c), 
    mValue(v), 
    mStop(false), 
    mMutex() 
{} 

void CtrThread::stop() 
{ 
    QMutexLocker ml(&mMutex); 
    mStop = true; 
} 

void CtrThread::run() 
{ 
    while(keeprunning()) 
    { 
     mCtr.add(mValue); 
    } 
} 

bool CtrThread::keeprunning() 
{ 
    QMutexLocker ml(&mMutex); 
    return ! mStop; 
} 

test.cpp

#include <QtCore> 
#include <QTest> 
#include "objs.h" 

int main(int argc, char** argv) 
{ 
    QCoreApplication app(argc, argv); 

    qDebug() << "Initalising"; 

    Counter ctr(0); 
    CtrThread thread_a(ctr, +1); 
    CtrThread thread_b(ctr, -1); 

    qDebug() << "Starting Threads"; 

    thread_a.start(); 
    thread_b.start(); 

    for (int i = 0; i != 15; ++i) 
    { 
     qDebug() << "Counter value" << ctr.add(0); 
     QTest::qSleep(1000); 
    } 

    qDebug() << "Stopping Threads"; 

    thread_a.stop(); 
    thread_b.stop(); 
    thread_a.wait(); 
    thread_b.wait(); 

    qDebug() << "Finished"; 
    return 0; 
} 

test.pro

QT=core testlib 
HEADERS=objs.h 
SOURCES=test.cpp objs.cpp 

Compilare ed eseguire, si vedrà il valore in fase di stampa, esempio di output:

Initalising 
Starting Threads 
Counter value 0 
Counter value 11057 
Counter value 28697 
Counter value 50170 
Counter value 60678 
Counter value 73773 
Counter value 84898 
Counter value 96441 
Counter value 118795 
Counter value 135293 
Counter value 146107 
Counter value 158688 
Counter value 169886 
Counter value 201203 
Counter value 212983 
Stopping Threads 
Finished 
+0

Hey @skyhisi grazie per l'ottima soluzione ... ma sto cercando 'QMutex' solo per scopi educativi. Voglio imparare il multi-threading in Qt passo dopo passo. Puoi darmi una semplice implementazione usando QMutex (per condividere 'counter' su due thread) –

3

OK, grazie speciale a @skyhisi per la grande soluzione per un vero progetto.

Ho letto il post di @skyhisi e altri articoli su QMutex e sulla condivisione delle variabili. Per scopi educativi ho implementato un semplice/chiaro esempio di utilizzo di QMutex per la condivisione della variabile (questo caso counter).

class thread_a : public QThread 
{ 
    Q_OBJECT 
public: 
    thread_a(QMutex*, int*); 
    void shutdown(); 

private: 
    QMutex* mutex; 
    int* counter; 
    bool isShutdownRequested; 

protected: 
    void run(); 
}; 

e in thread_a.cpp di file:

thread_a::thread_a(QMutex * m, int* i) 
{ 
    counter=i; 
    isShutdownRequested=false; 
    mutex=m; 
} 

void thread_a::run() 
{ 
    isShutdownRequested=false; 
    forever{ 
     //lock mutex for changing in shared variable 
     mutex->lock(); 
     *counter=*counter+1; 
     mutex->unlock(); 

     if(isShutdownRequested) 
      break; 
    } 
} 

void thread_a::shutdown() 
{ 
    isShutdownRequested=true; 
} 

In myMainWindow::RunThreads(bool bl) Slot:

int cnt=0; 
QMutex mu; 
thread* a=new thread_a(&mu, &cnt); 
thread* b=new thread_b(&mu, &cnt); 
a.start(); 
b.start(); 

In myMainWindow::~myMainWindow() decostruttore:

a->shutdown(); 
b->shutdown(); 

Grazie @skyh ancora isi

1

Scrivo un semplice esempio riferito alla "guida" di QMutex, in cui due thread cambiano uno stesso numero (come un monitor). Riflette anche il codice di S.M.Mousavi. Ecco il codice:

//main.cpp

#include <QCoreApplication> 
#include "method.h" 

int aNum=0; 
QMutex aMutex; 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    int *p_no= &aNum; 
    QMutex *p_Mu = &aMutex; 

    method mThread1(p_Mu, p_no); 
    method mThread2(p_Mu, p_no); 

    mThread1.name = "one"; 
    mThread2.name = "two"; 

    mThread1.start(); 
    mThread2.start(); 

    return a.exec(); 
} 

// method.h

#ifndef METHOD_H 
#define METHOD_H 

#include <QDebug> 
#include <QThread> 
#include <QtCore> 
#include <QString> 
#include <QMutex> 


class method: public QThread 
{ 
public: 
    method(QMutex *mu, int *nu); 
    void run(); 
    void method1(); 
    void method2(); 
    QString name; 

private: 
    int *number; 
    QMutex *myMutex; 
}; 

#endif // METHOD_H 

//method.cpp #include "method.h"

method::method(QMutex *mu, int *nu) 
{ 
    myMutex = mu; 
    number = nu; 
} 


void method:: run() 
{ 
    for (int i = 0; i<100; i++) 
    { 
     if(this->name == "one") 
     { 
      this->method1(); 
     } 
     else 
     { 
      this->method2(); 
     } 
    } 
} 

void method::method1() 
{ 
    myMutex->lock(); 
    *number += 1; 
    qDebug()<<*number<<"---"<<this->name; 
    myMutex->unlock(); 
} 

void method ::method2() 
{ 
    myMutex->lock(); 
    *number -= 1; 
    qDebug()<<*number<<"---"<<this->name; 
    myMutex->unlock(); 
}