0

台形公式とマルチスレッド を使用して積分を計算する必要があります。

Javaプールスレッドの例を使用して作成したプールスレッドを使用しています。

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

各間隔は、積分も計算するコンテナ「セグメント」に入れられます。
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ます。低くない場合は、とに対して同じアルゴリズムを実行しamますmb。等

#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クラスはすべてを結び付けます。プールを作成し、合計を含み、最初の間隔でプールでexecuteを呼び出すことによって計算を開始します。また、デバッグ用のカウンターもあります。

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

約半分の時間で積分を正しく計算します。残りの半分は、予想よりも多くの数を取得します(これは異なります)。数値が大きくなると、一部の間隔が2回計算されていることに気付きます。間違えなければ、コードをスレッドセーフにしたので、これがどのように発生するのかわかりません。私はマルチスレッドプログラミングにかなり慣れていないので、ミューテックスで何か問題が発生している可能性がありますか?または、Javaプールからの移行が間違っている可能性がありますか?
もう1つは、main.cppにあります。積分の計算がいつ完了するかわからないため、結果を正しく表示する方法がわかりません。マネージャーを含むスレッドでwait(2500)関数を使用していますが、計算時間はPCや関数によって異なる可能性があるため、あまり適切な方法ではありません。

あなたが提供することができるどんな助けにも前もって感謝します。

4

1 に答える 1

0

ロックを間違えました。指摘したJavaの例では、同じロック(queueそれ自体)がエンキュー(in execute)とデキュー(ワーカースレッド)で使用されています。そうすれば、キュー操作は本当にスレッドセーフです。残念ながら、コードでは2つの異なるロックを使用しています。Pool::poolMutexenque(in execute)およびPoolWorker::mutexdeque(PoolWorkerスレッド)。このように、スレッド間の両端キューに対してのみキューを保護しますが、両端キューと両端キューは同時に発生する可能性があります。は1つのスレッドだけで呼び出されているため、あなたPool::poolMutexは役に立たないのでexecute、1つのスレッドだけでロックおよびロック解除されています。enqueとdequeには、1つの同じミューテックスのみを使用する必要があります。Pool::poolMutexコンストラクターを介してPoolWorkerに渡し、の代わりにロックしPoolWorker::mutexます。

したがって、enqueを実行し、一部のスレッドが機能を終了した直後に、(キューが空ではないため)すぐにdequeし、を待機しませんwakeOne。次にwakeOne、別のスレッドを起動します。2つのスレッドが同じジョブを取得する(クラッシュしない)方法について明確な説明はありませんが、Javaのオリジナルのように1つのロックのみを使用すると、コードは確実にうまく機能します。

于 2012-09-21T21:09:14.200 に答える