2012-09-20 62 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