Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/qt/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从多个QThread更新QProgressBar_C++_Qt_Qthread_Qprogressbar - Fatal编程技术网

C++ 从多个QThread更新QProgressBar

C++ 从多个QThread更新QProgressBar,c++,qt,qthread,qprogressbar,C++,Qt,Qthread,Qprogressbar,我在网上找到了几个教程,解释了如何在长时间的计算过程中更新QProgressBar。其中之一是:使用QThread进行计算,然后发出一个连接到progressBar.setValue(int)的信号 我认为这对同时运行的几个QThread也一定有效,但有些地方工作不正常 所以,我要做的是:我想计算几个粒子的轨迹(每个粒子都有一个长环)。为了使用多核处理,我为每个粒子创建了一个QThread,并让它调用相应的计算方法。这样做很好,使用了所有的核心,计算完成的时间大约比以前缩短了四分之一 我根据本教

我在网上找到了几个教程,解释了如何在长时间的计算过程中更新QProgressBar。其中之一是:使用QThread进行计算,然后发出一个连接到
progressBar.setValue(int)
的信号

我认为这对同时运行的几个QThread也一定有效,但有些地方工作不正常

所以,我要做的是:我想计算几个粒子的轨迹(每个粒子都有一个长环)。为了使用多核处理,我为每个粒子创建了一个QThread,并让它调用相应的计算方法。这样做很好,使用了所有的核心,计算完成的时间大约比以前缩短了四分之一

我根据本教程编写了一个工人类。标题如下所示: (工人h)

在world.cpp中,我有一个启动所有线程的函数
run
,还有一个由Worker调用的函数
runParticle

void World::run(double deltaTau, double maxDist, int iterations)
{
    globalProgress = 0;
    for (int j = 0; j < particles->size(); j++) { //loop over all particles
        QThread *thread = new QThread;
        Worker *worker = new Worker(this, deltaTau, maxDist, iterations, j);
        worker->moveToThread(thread);
        connect(thread, SIGNAL(started()), worker, SLOT(process()));
        connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
        connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
        thread->start();
    }
}

void World::runParticle(double deltaTau, double maxDist, int iterations, int index)
{
    for (int i = 0; i < iterations; i++) { //loop over iteration steps
        if (i % 1000 == 0) { //only update the progress bar every 1000th iteration
            emit updateProgress(++globalProgress);
            qApp->processEvents(); // <--- I added this line, no effect!
        }
        [...] // <--- do my calculations for the particle's trajectories
    }
}
我的问题是,直到所有的计算完成,进度条才移动,然后我看到它很快移动到100%

有没有人看到我的错误,或者知道如何正确地做到这一点

编辑

我做了以下修改:

(工人h)

(世界的一部分,cpp)


正如我在上面的代码中所标记的那样,当线程完成时,我永远不会收到通知,我最终会在主窗口中进入一个无限循环。World Worker连接是否仍有问题?

问题在于,为了处理发出的信号,需要允许事件循环在新线程上运行。通过保持runParticle中的for循环,在函数完成之前,这不会发生

有一种简单的方法可以解决这个问题,即在循环期间每隔一段时间调用QApplication::processEvents一次

更好的方法是重新设计对象,以便在退出并允许事件循环自然运行之前处理大量迭代

因此,要创建用于处理的时间片,在for循环中,您需要计算迭代所花费的时间。如果时间超过1/30秒,则调用QueuedConnection类型的信号以再次调用插槽函数并退出for循环

QueuedConnection将确保处理所有事件,然后再次调用函数

假设runParticle是一个插槽:-

void Worker::runParticle(...)
{
    static int i = 0;

    QDateTime start = QDateTime::currentDateTime();

    for(i; i < iteration; ++i)
    {
        // do processing


        emit updateProgress(++globalProgress);

        // check if we've been here too long
        if(QDateTime::currentDateTime() > start.addMSecs(300))
        {
          emit ProcessParticle(); // assuming this is connected to runParticle with a Queued Connection
          ++i; // ensure we return to the next iteration
          return;
        }
    }
}
void Worker::runParticle(…)
{
静态int i=0;
QDateTime start=QDateTime::currentDateTime();
对于(i;i<迭代;++i)
{
//处理
发出updateProgress(++globalProgress);
//看看我们是否在这里呆得太久了
如果(QDateTime::currentDateTime()>start.addMSecs(300))
{
emit ProcessParticle();//假设它通过排队连接连接到runParticle
++i、 //确保我们返回到下一个迭代
返回;
}
}
}
另一方面,当一个对象被移动到一个新线程时,它的所有子线程也被移动,其中一个子线程是QObject层次结构的一部分

通过让Worker对象持有指向世界的指针,它直接调用世界的runParticle函数,该函数仍在主线程上。虽然这是不安全的,但也意味着runParticle函数正在主线程上处理


您需要将runParticle函数移动到新线程上的Worker对象。

IMO这是错误的。创建一个线程的成本很高,您需要创建很多线程。 首先,您应该使用,您的案例完全匹配这个类的功能


还有一些方法可以大幅度减少boiler plate代码(这是Qt的一个被放弃的功能,因此建议使用QThreadPool,但您可以尝试一下,它工作得很好)。

事实上,位于单独线程中的
工作者调用了
世界上的一个方法,它生活在主线中,试图发出信号,这让我很紧张。
updateProgress
信号实际上应该是worker上的一个信号,因此
connect
调用将其识别为一个跨线程信号。不确定这是否是问题所在,但根据我对Qt线程模型的了解,您的代码不符合犹太规范。您的
World
Worker
类似乎紧密耦合。考虑重新设计<代码> Word 类,这样它就不会对你的<代码> Works/Cuff>类一无所知,也不会了解迭代。将迭代和信号发射移动到
Worker
类,然后将其连接到进度条。如您所见,我已经尝试过qApp->processEvents(),QApplication::processEvents()不会改变循环中的任何内容。你的其他建议听起来很有趣,但我不知道如何建立排队连接!?QueuedConnection是传递给调用以连接的参数。看到了吗,我把runParticle设为一个槽,并添加了一个新的与该参数相关的信号处理粒子?我在哪里连接这些?我已经编辑了我的答案。请阅读下面的示例代码。至于连接,您可以直接在Worker类构造函数中进行:connect(this,&Worker::ProcessParticles,this,&Worker::runParticle,Qt::QueuedConnection);啊,好吧,但这有点烦人,因为计算需要访问一个类粒子(我还没有展示)中的很多东西,我在世界上有一个QList。我会尝试将这些东西移到Worker中…以前从未听说过QThreadPool,但它听起来确实像我应该在这里使用的东西。到目前为止,我“只”模拟了100个粒子,即一次创建100个线程,但它还没有崩溃。如果我尝试1000个粒子,它会立即崩溃。希望你能解决这个问题…@Bianfable,我刚刚注意到了这一点。这里的问题不是QThread的使用,而是如何使用它。您不需要比处理器内核更多的线程
void World::run(double deltaTau, double maxDist, int iterations)
{
    globalProgress = 0;
    for (int j = 0; j < particles->size(); j++) { //loop over all particles
        QThread *thread = new QThread;
        Worker *worker = new Worker(this, deltaTau, maxDist, iterations, j);
        worker->moveToThread(thread);
        connect(thread, SIGNAL(started()), worker, SLOT(process()));
        connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
        connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()));
        connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
        thread->start();
    }
}

void World::runParticle(double deltaTau, double maxDist, int iterations, int index)
{
    for (int i = 0; i < iterations; i++) { //loop over iteration steps
        if (i % 1000 == 0) { //only update the progress bar every 1000th iteration
            emit updateProgress(++globalProgress);
            qApp->processEvents(); // <--- I added this line, no effect!
        }
        [...] // <--- do my calculations for the particle's trajectories
    }
}
progressBar->setValue(0);
progressBar->setMaximum(nrPart * iter / 1000); //number of particles * number of iteration steps / 1000
connect(world, SIGNAL(updateProgress(int)), progressBar, SLOT(setValue(int)));
world->run(timeStep, dist, iter);
#include "world.h"

class Worker: public QObject
{
    Q_OBJECT

public:
    explicit Worker(World *world = 0, Particle *particle = 0, QList<MagneticField> *bfields = 0, double deltaTau = 0., double maxDist = 0., int iterations = 0);

public slots:
    void process();

signals:
    void finished();
    void updateProgress(int value);
    void ProcessParticle();
    void eror(QString err);

private:
    int i;
    Particle *p;
    QList<MagneticField> *magneticFields;
    double my_deltaTau;
    double my_maxDist;
    int my_iterations;
};
#include "worker.h"

Worker::Worker(World *world, Particle *particle, QList<MagneticField> *bfields, double deltaTau, double maxDist, int iterations)
{
    i = 0;
    const World *w = world;
    p = particle;
    magneticFields = bfields;
    my_deltaTau = deltaTau;
    my_maxDist = maxDist;
    my_iterations = iterations;
    connect(this, SIGNAL(updateProgress(int)), w, SLOT(updateTheProgress(int)));
    connect(this, SIGNAL(ProcessParticle()), this, SLOT(process()), Qt::QueuedConnection);
}

void Worker::process()
{
    const int modNr = my_iterations / 1000;
    QDateTime start = QDateTime::currentDateTime();
    while (i < my_iterations) { //loop over iteration steps
        [...] // <--- do my calculations
        //handle progress
        emit updateProgress(1);
        if (QDateTime::currentDateTime() > start.addMSecs(300)) {
            emit ProcessParticle();
            ++i; //ensure we return to the next iteration
            return;
        }
        i++;
    }
    qDebug() << "FINISHED"; // <--- I can see this, so finished() should be emitted now...
    emit finished();
}
public slots:
    void threadFinished();
    void updateTheProgress(int value);

signals:
    void updateProgress(int value);
void World::threadFinished()
{
    particleCounter++;
    qDebug() << "particles finished: " << particleCounter; // <--- this is NEVER called !?!?
    if (particleCounter == particles->size()) {
        hasRun = true;
    }
}

void World::updateTheProgress(int value)
{
    globalProgress += value;
    emit updateProgress(globalProgress);
}

void World::run(double deltaTau, double maxDist, int iterations)
{
    globalProgress = 0;
    particleCounter = 0;
    hasRun = false;
    for (int i = 0; i < particles->size(); i++) { //loop over all particles
        QThread *thread = new QThread;
        Worker *worker = new Worker(this, &(*particles)[i], bfields, deltaTau, maxDist, iterations);
        worker->moveToThread(thread);
        connect(thread, SIGNAL(started()), worker, SLOT(process()));
        connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
        connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()));
        connect(worker, SIGNAL(finished()), this, SLOT(threadFinished())); // <--- this connection SHOULD make sure, I count the finished threads
        connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
        thread->start();
    }
}
progressBar->setValue(0);
progressBar->setMaximum(nrPart * iter);
connect(world, SIGNAL(updateProgress(int)), progressBar, SLOT(setValue(int)));
world->run(timeStep, dist, iter);
while (!world->hasBeenRunning()) {} //wait for all threads to finish
void Worker::runParticle(...)
{
    static int i = 0;

    QDateTime start = QDateTime::currentDateTime();

    for(i; i < iteration; ++i)
    {
        // do processing


        emit updateProgress(++globalProgress);

        // check if we've been here too long
        if(QDateTime::currentDateTime() > start.addMSecs(300))
        {
          emit ProcessParticle(); // assuming this is connected to runParticle with a Queued Connection
          ++i; // ensure we return to the next iteration
          return;
        }
    }
}