2012-09-20 75 views
0

我需要使用trapezoidal rule和多線程來計算積分。qt多線程:計算整數

我正在使用使用java pool thread example編寫的池線程。

#ifndef POOL_H 
#define POOL_H 
#include <QObject> 
#include <QThread> 
#include <QWaitCondition> 
#include <QMutex> 
#include <QQueue> 
#include "poolworker.h" 
#include "segment.h" 

class Segment; 
class PoolWorker; 

class Pool: public QObject 
{ 
    Q_OBJECT 
public: 
    explicit Pool(QObject *parent = 0); 
    Pool(int nThreads); 
    void execute(Segment *s); 
    static QWaitCondition con; 
    static QMutex poolMutex; 
    static QQueue<Segment*> segmentQueue; 
private: 
    int nThreads; 
    QVector<PoolWorker*> workers; 
}; 

#endif // POOL_H 


#include "pool.h" 

QWaitCondition Pool::con; 
QQueue<Segment*> Pool::segmentQueue; 
QMutex Pool::poolMutex; 

Pool::Pool(QObject *parent) : 
    QObject(parent) 
{ 
} 


Pool::Pool(int nThreads) 
{ 
    this->nThreads = nThreads; 
    for (int i = 0; i < nThreads; i++) 
    { 
     workers.push_back(new PoolWorker()); 
     workers[i]->start(); 
    } 
} 


void Pool::execute(Segment *s) 
{ 
    poolMutex.lock(); 
    segmentQueue.enqueue(s); 
    con.wakeOne(); 
    poolMutex.unlock(); 
} 


#ifndef POOLWORKER_H 
#define POOLWORKER_H 

#include <QThread> 
#include <QMutex> 
#include "segment.h" 
#include "pool.h" 

class PoolWorker : public QThread 
{ 
    Q_OBJECT 
public: 
    explicit PoolWorker(QObject *parent = 0); 
    void run(); 
    static QMutex mutex; 
signals: 

public slots: 

private: 

}; 

#endif // POOLWORKER_H 


#include "poolworker.h" 

QMutex PoolWorker::mutex; 

PoolWorker::PoolWorker(QObject *parent) : 
    QThread(parent) 
{ 
} 

void PoolWorker::run() 
{ 
    Segment *temp; 
    forever 
    { 
     mutex.lock(); 
     while(Pool::segmentQueue.isEmpty()) 
     { 
      Pool::con.wait(&mutex); 
     } 

     temp = Pool::segmentQueue.dequeue(); 
     mutex.unlock(); 
     temp->doWork(); 
    } 
} 

每個間隔放入一個容器「Segment」中,它也計算積分。
Sab = 0.5*(b-a)*(f(a)+f(b))
m = (a+b)/2.0
Sam = 0.5*(m-a)*(f(a)+f(m))
Smb = 0.5*(b-m)*(f(b)+f(m))
如果SabSam+Smb之間的差大於Eps下,然後我添加Sab使用Manager::addSum積分總和。如果它不低,我對ammb執行相同的算法。等

#ifndef SEGMENT_H 
#define SEGMENT_H 

#include <QObject> 
#include <cmath> 
#include "manager.h" 
#include <QDebug> 

class Segment : public QObject 
{ 
    Q_OBJECT 
private: 
    double a,b,Sab,Sam,Smb,m,Eps; 
    double f(double x); 
public: 
    explicit Segment(QObject *parent = 0); 
    Segment(double a, double b); 
    void doWork(); 
signals: 

public slots: 
}; 

#endif // SEGMENT_H 


#include "segment.h" 


Segment::Segment(QObject *parent) : 
    QObject(parent) 
{ 
} 

Segment::Segment(double a, double b) 
{ 
    this->a = a; 
    this->b = b; 
    Eps = 0.001; 
} 

void Segment::doWork() 
{ 
    Sab = 0.5*(b-a)*(f(a)+f(b)); 
    m = (a+b)/2.0; 
    Sam = 0.5*(m-a)*(f(a)+f(m)); 
    Smb = 0.5*(b-m)*(f(b)+f(m)); 
    if (fabs(Sab - (Sam + Smb)) <= Eps) 
    { 
     Manager::addSum(Sab); 
     qDebug() << "Reached Eps on interval a= " << a << ",b = " << b 
       << ", return S+= " << Sab; 
     Manager::inc(); 
    } 
    else 
    { 
     Manager::threadPool->execute(new Segment(a,m)); 
     Manager::threadPool->execute(new Segment(m,b)); 
    } 
} 


double Segment::f(double x) 
{ 
    return pow(x,3.0) - 4.0*pow(x,2.0) + 6.0*x - 24.0; 
} 

Manager類綁一切:它創建池,包含的總和,並通過調用執行上池與第一時段開始計算。它還有一個用於調試的計數器。

#ifndef MANAGER_H 
#define MANAGER_H 

#include <QObject> 
#include <QThread> 
#include <QQueue> 
#include <QVector> 
#include "segment.h" 
#include "pool.h" 

class Pool; 

class Manager : public QObject 
{ 
    Q_OBJECT 
private: 
    static double sum; 
    static int i; 
    static QMutex mutex; 
public: 
    explicit Manager(QObject *parent = 0); 
    static Pool *threadPool; 
    static void addSum(double add); 
    static void inc(); 
    double viewSum(); 
    int viewCount(); 
    void doSetup(QThread &thread); 
signals: 

public slots: 
    void doWork(); 
}; 

#endif // MANAGER_H 


#include "manager.h" 

double Manager::sum = 0; 
int Manager::i = 0; 
Pool* Manager::threadPool = new Pool(10); 
QMutex Manager::mutex; 

Manager::Manager(QObject *parent) : 
    QObject(parent) 
{ 
} 

void Manager::addSum(double add) 
{ 
    mutex.lock(); 
    sum += add; 
    mutex.unlock(); 
} 

void Manager::inc() 
{ 
    i++; 
} 

double Manager::viewSum() 
{ 
    return sum; 
} 

int Manager::viewCount() 
{ 
    return i; 
} 

void Manager::doSetup(QThread &thread) 
{ 
    connect(&thread,SIGNAL(started()),this,SLOT(doWork())); 
} 

void Manager::doWork() 
{ 
    threadPool->execute(new Segment(4.5,12.0)); 
} 

在主要我創建經理,管理器的線程和顯示結果。

#include <QCoreApplication> 
#include <QDebug> 
#include <QThread> 
#include <QTimer> 
#include "manager.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 
    Manager man; 
    QThread manThread; 
    man.doSetup(manThread); 
    man.moveToThread(&manThread); 
    manThread.start(); 
    manThread.wait(2500); 
    qDebug() << "integrate(x^3 - 4*x^2 + 6*x - 24) from 4.5 to 12.0 = " 
      << man.viewSum(); 
    qDebug() << "i =" << man.viewCount(); 
    manThread.quit(); 
    QTimer::singleShot(1000, &a, SLOT(quit())); 
    return a.exec(); 
} 

它在大約一半的時間內正確計算積分。另一半我得到比預期更大的數字(各不相同)。當我得到一個更大的數字時,我注意到有些間隔計算了兩次。如果我沒有弄錯,我已經讓代碼是線程安全的,所以我不明白這是怎麼發生的。我對於多線程編程相當陌生,所以我可能會在互斥體上做錯事情?或者,也許我從Java池轉換是錯誤的?
另一件事是在main.cpp我不知道如何正確顯示結果,因爲我不知道什麼時候積分完成計算。我在包含管理器的線程上使用了wait(2500)函數,但它不是一個很好的方法,因爲計算時間可能因不同的PC和不同的功能而異。

在此先感謝您提供的任何幫助。

回答

0

你的鎖定錯了。在您指出的Java示例中,排隊(在execute中)和出隊(在工作線程中)使用相同的鎖(queue本身)。這樣,隊列操作確實是線程安全的。在你的代碼中不幸的是,你使用了兩個不同的鎖。 Pool::poolMutex for enque(在execute)和PoolWorker::mutex for deque(在PoolWorker線程中)。這樣,你只在線程之間守護隊列,但deque和enque可以同時發生。你的Pool::poolMutex是無用的,因爲execute只被一個線程調用,所以它只能被一個線程鎖定和解鎖。你只需要爲enque和deque使用同一個互斥體。通過構造函數將Pool::poolMutex傳遞給PoolWorker並將其鎖定,而不是PoolWorker::mutex

所以當你enque和一些線程剛剛完成工作,它會立即deque(因爲隊列不空),而不是等待你的wakeOne。然後在wakeOne上,你會發射另一個線程。對於這兩個線程如何獲得同樣的工作(而不是崩潰),我沒有一個明確的解釋,但是如果您只使用Java原始版中的一個鎖,那麼您的代碼肯定會更好地工作。

+0

謝謝!我只是測試它,現在它似乎正在工作。 – Urdak