基准QTableWidget的多线程输入,奇怪的结果? [英] Benchmarking QTableWidget for multi threaded input, strange result?

查看:533
本文介绍了基准QTableWidget的多线程输入,奇怪的结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的程序中,我正在从一个文本文件&把它放到一个QTableWidget。
最初,它是在GUI线程&然后我决定多线程它有更好的性能。但相反,性能是非常慢,即8倍慢!
所以我决定基准。这是我的文件:



main.cpp

  #include< ; QtGui / QApplication> 
#include< QMutex>
#include< QDir>
#include< string>
#include< QDebug>
#includemainwindow.h

QFile * logFile = NULL;
QTextStream * logStream = NULL;
QMutex * mutex = NULL;
bool * debugMode = NULL;

void myMessageOutput(QtMsgType type,const char * msg)
{
if((logFile!= NULL)& b $ b {
mutex-> lock();
switch(type)
{
case QtDebugMsg:
if(!* debugMode)
{
mutex-> unlock
return;
}
* logStream<< msg;
logStream-> flush();
break;
case QtWarningMsg:
* logStream<< \\\
*** Warning *** \\\
;
* logStream<< msg;
* logStream<< \\\
***警告完成*** \\\
;
logStream-> flush();
break;
case QtCriticalMsg:
* logStream<< \\\
*** Critical *** \\\
;
* logStream<< msg;
* logStream<< \\\
*** Critical Complete *** \\\
;
logStream-> flush();
break;
case QtFatalMsg:
* logStream<< \\\
*** Fatal *** \\\
;
* logStream<< msg;
* logStream<< \\\
*** Fatal Complete *** \\\
;
logStream-> flush();
abort();
}
mutex-> unlock();
}
}

无效CreateLogFile()
{
QString path =C:\\Users\\\abcd\\\ \\Documents\\QT\\benchmark\\output.log;
QFile * file = new QFile(path);
if(file-> exists())
file-> remove();
if(!file-> open(QFile :: WriteOnly | QFile :: Text))
{
qFatal(Could not create log file。
}

logStream = new QTextStream(file);
logStream-> setRealNumberNotation(QTextStream :: FixedNotation);
logStream-> setRealNumberPrecision(16);
logFile = file;
}

int main(int argc,char * argv [])
{
mutex = new QMutex();
qInstallMsgHandler(myMessageOutput);
debugMode = new bool;
CreateLogFile();
* debugMode = true;

QApplication a(argc,argv);
MainWindow w;
w.show();
w.bench2();

return a.exec();

}



mainwindow.h



  #ifndef MAINWINDOW_H 
#define MAINWINDOW_H

#include< QMainWindow>
#include< QThread>
#includemulti_thread.h

命名空间Ui {
class MainWindow;
}

class MainWindow:public QMainWindow
{
Q_OBJECT

public:
explicit MainWindow(QWidget * parent = 0 );
〜MainWindow();
void bench2();

private:
Ui :: MainWindow * ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

  #includemainwindow.h
#includeui_mainwindow.h
#include< QDebug&
#include< QList>
#include< QTime>

MainWindow :: MainWindow(QWidget * parent):QMainWindow(parent),ui(new Ui :: MainWindow)
{
ui-> setupUi(this);
}

MainWindow ::〜MainWindow()
{
delete ui;
}

void MainWindow :: bench2()
{
QTableWidget * table = new QTableWidget();
table-> setRowCount(1000);
table-> setColumnCount(1000);
for(int i = 0; i <1000; i ++)
{
for(int j = 0; j <1000; j ++)
table-> setItem (i,j,new QTableWidgetItem());
}
Multi_Thread ** multis = new Multi_Thread * [4];
QThread ** thrs = new QThread * [4];
int from;
int to = -1;

QTime时间;
time.start();

for(int i = 0; i <4; i ++)
{
from = to + 1;
to = from + 250;
if(i == 3)
to = 999;

multis [i] = new Multi_Thread();
multis [i] - > setTable(table,from,to);
thrs [i] = new QThread();
connect(thrs [i],SIGNAL(started()),multis [i],SLOT(bench2_1()))
multis [i] - > moveToThread(thrs [i]);
}

for(int i = 0; i <4; i ++)
{
if(!i)
time.start ;

thrs [i] - > start();
}

for(int i = 0; i <4; i ++)
{
thrs [i] - > wait
}
qDebug()<< \\\
bench2 1<时间流逝();

for(int i = 0; i <4; i ++)
{
delete multis [i];
delete thrs [i];
}
delete [] multis;
delete [] thrs;


table-> clear();
table-> setRowCount(1000);
table-> setColumnCount(1000);
for(int i = 0; i <1000; i ++)
{
for(int j = 0; j <1000; j ++)
table-> setItem (i,j,new QTableWidgetItem())
}

time.start();

for(int i = 0; i <1000; i ++)
{
for(int j = 0; j <1000; j ++)
- > item(i,j) - > setText(0);
}
qDebug()<< \\\
bench2 2<时间流逝();

table-> clear();
table-> setRowCount(1000);
table-> setColumnCount(1000);
QTableWidgetItem *** items = new QTableWidgetItem ** [1000];
for(int i = 0; i <1000; i ++)
{
items [i] = new QTableWidgetItem * [1000];
for(int j = 0; j< 1000; j ++)
{
QTableWidgetItem * item = new QTableWidgetItem();
table-> setItem(i,j,item);
items [i] [j] = item;
}
}

multis = new Multi_Thread * [4];
thrs = new QThread * [4];
to = -1;

for(int i = 0; i <4; i ++)
{
from = to + 1;
to = from + 250;
if(i == 3)
to = 999;

multis [i] = new Multi_Thread();
multis [i] - > setItems(items,from,to);
thrs [i] = new QThread();
connect(thrs [i],SIGNAL(started()),multis [i],SLOT(bench2_2()
multis [i] - > moveToThread(thrs [i]);
}
table-> blockSignals(true);
table-> setUpdatesEnabled(false);
table-> setWordWrap(false);

for(int i = 0; i <4; i ++)
{
if(!i)
time.start

thrs [i] - > start();
}

for(int i = 0; i <4; i ++)
{
thrs [i] - > wait
}
qDebug()<< \\\
bench2 3<时间流逝();
table-> blockSignals(false);
table-> setUpdatesEnabled(true);
table-> setWordWrap(true);

for(int i = 0; i <4; i ++)
{
delete multis [i];
delete thrs [i];
}
delete [] multis;
delete [] thrs;

table-> clear();
for(int i = 0; i <1000; i ++)
{
delete [] items [i];
}
delete [] items;

}



multi_thread.h



  #ifndef MULTI_THREAD_H 
#define MULTI_THREAD_H

#include< QObject>
#include< QThread>
#include< QTableWidget>

class Multi_Thread:public QObject
{
Q_OBJECT
public:
explicit Multi_Thread();
void setTable(QTableWidget * tab,int f,int t);
void setItems(QTableWidgetItem *** i,int f,int t);

private:
QTableWidget * table;
QTableWidgetItem *** items;
int from;
int to;

信号:

公共位置:
void bench2_1();
void bench2_2();

};

#endif // MULTI_THREAD_H

multi_thread.cpp

  #includemulti_thread.h

Multi_Thread :: Multi_Thread():QObject()
{
}

void Multi_Thread :: setTable(QTableWidget * tab,int f,int t)
{
table = tab;
from = f;
to = t;
}

void Multi_Thread :: setItems(QTableWidgetItem *** i,int f,int t)
{
items = i;
from = f;
to = t;
}

void Multi_Thread :: bench2_1()
{
for(int i = from; i <=到; i ++)
{
for(int j = 0; j <1000; j ++)
{
table-> item(i,j) - > setText(0);
}
}
QThread :: currentThread() - > exit(0);
}

void Multi_Thread :: bench2_2()
{
for(int i = from; i <=到; i ++)
{
for(int j = 0; j <1000; j ++)
{
items [i] [j] - > setText(0);
}
}
QThread :: currentThread() - > exit(0);
}

output.log

  bench2 1 7654 
bench2 2 1160
bench2 3 8021

奇怪的是,我期望bench2 3比bench2 1更快。



PS:我的笔记本电脑硬件可能需要4线程达到100%的使用率。请根据您的硬件要求进行编辑。

解决方案

调用 table-> item(...) > setText()来自任何线程而不是GUI线程是未定义的行为。不要这样做。很好,你在 QThread 中使用 QObject 来完成这项工作,但是你不能直接调用方法



内部模型将调用视图的 dataChanged()您的更新。除了每个项目的内存分配之外,这是减速的可能来源。



在独立线程中准备数据模型的典型方法是:


  1. 在一个单独的线程中实例化模型。此时,模型不能连接到任何视图。用数据初始化模型。它可以是 QStandardItemModel 或自定义模型。自定义模型并不那么难,如果不需要为每个项目执行内存分配,通常可以更高效。

    code>)。


  2. 只能从存在的线程中访问模型,除非你使用排队方法调用或排队信号 - 槽连接。该视图使用后者,因此可以位于与模型分开的线程中。


A QTableWidget 使用 QTableView 捆绑内部 QStandardItemModel 。如果你想单独处理模型,你只需使用模型类与视图类隔离。


In my program I am reading a lot of data from a text file & putting it to a QTableWidget. Initially it was all done in the GUI thread & then I decided to multi thread it to have better performance. But on the contrary the performance was significantly slow i.e. 8x slower! So I decided to benchmark it. Here are my files:

main.cpp

#include <QtGui/QApplication>
#include <QMutex>
#include <QDir>
#include <string>
#include <QDebug>
#include "mainwindow.h"

QFile *logFile = NULL;
QTextStream *logStream = NULL;
QMutex *mutex = NULL;
bool *debugMode = NULL;

void myMessageOutput(QtMsgType type, const char *msg)
 {
    if(((logFile != NULL) && (debugMode != NULL)))
    {
        mutex->lock();
         switch (type)
         {
         case QtDebugMsg:
             if(!*debugMode)
             {
                 mutex->unlock();
                 return;
             }
             *logStream << msg;
             logStream->flush();
             break;
         case QtWarningMsg:
             *logStream << "\n*** Warning ***\n";
             *logStream << msg;
             *logStream << "\n*** Warning Complete ***\n";
             logStream->flush();
             break;
         case QtCriticalMsg:
             *logStream << "\n*** Critical ***\n";
             *logStream << msg;
             *logStream << "\n*** Critical Complete ***\n";
             logStream->flush();
             break;
         case QtFatalMsg:
             *logStream << "\n*** Fatal ***\n";
             *logStream << msg;
             *logStream << "\n*** Fatal Complete ***\n";
             logStream->flush();
             abort();
         }
         mutex->unlock();
    }
 }

void CreateLogFile()
{
    QString path = "C:\\Users\\abcd\\Documents\\QT\\benchmark\\output.log";
    QFile *file = new QFile(path);
    if(file->exists())
        file->remove();
    if(!file->open(QFile::WriteOnly | QFile::Text))
    {
        qFatal("Could not create log file.");
    }

    logStream = new QTextStream(file);
    logStream->setRealNumberNotation(QTextStream::FixedNotation);
    logStream->setRealNumberPrecision(16);
    logFile = file;
}

int main(int argc, char *argv[])
{
    mutex = new QMutex();
    qInstallMsgHandler(myMessageOutput);
    debugMode = new bool;
    CreateLogFile();
    *debugMode = true;

    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.bench2();

    return a.exec();

}

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QThread>
#include "multi_thread.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void bench2();

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QList>
#include <QTime>

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::bench2()
{
    QTableWidget *table = new QTableWidget();
    table->setRowCount(1000);
    table->setColumnCount(1000);
    for(int i = 0; i < 1000; i++)
    {
        for(int j = 0; j < 1000; j++)
            table->setItem(i, j, new QTableWidgetItem());
    }
    Multi_Thread **multis = new Multi_Thread *[4];
    QThread **thrs = new QThread *[4];
    int from;
    int to = -1;

    QTime time;
    time.start();

    for(int i = 0; i < 4; i++)
    {
        from = to + 1;
        to = from + 250;
        if(i == 3)
            to = 999;

        multis[i] = new Multi_Thread();
        multis[i]->setTable(table, from, to);
        thrs[i] = new QThread();
        connect(thrs[i], SIGNAL(started()), multis[i], SLOT(bench2_1()));
        multis[i]->moveToThread(thrs[i]);
    }

    for(int i = 0; i < 4; i++)
    {
        if(!i)
            time.start();

        thrs[i]->start();
    }

    for(int i = 0; i < 4; i++)
    {
        thrs[i]->wait();
    }
    qDebug() << "\nbench2 1 " << time.elapsed();

    for(int i = 0; i < 4; i++)
    {
        delete multis[i];
        delete thrs[i];
    }
    delete[] multis;
    delete[] thrs;


    table->clear();
    table->setRowCount(1000);
    table->setColumnCount(1000);
    for(int i = 0; i < 1000; i++)
    {
        for(int j = 0; j < 1000; j++)
            table->setItem(i, j, new QTableWidgetItem());
    }

    time.start();

    for(int i = 0; i < 1000; i++)
    {
        for(int j = 0; j < 1000; j++)
            table->item(i, j)->setText("0");
    }
    qDebug() << "\nbench2 2 " << time.elapsed();

    table->clear();
    table->setRowCount(1000);
    table->setColumnCount(1000);
    QTableWidgetItem ***items = new QTableWidgetItem **[1000];
    for(int i = 0; i < 1000; i++)
    {
        items[i] = new QTableWidgetItem *[1000];
        for(int j = 0; j < 1000; j++)
        {
            QTableWidgetItem *item = new QTableWidgetItem();
            table->setItem(i, j, item);
            items[i][j] = item;
        }
    }

    multis = new Multi_Thread *[4];
    thrs = new QThread *[4];
    to = -1;

    for(int i = 0; i < 4; i++)
    {
        from = to + 1;
        to = from + 250;
        if(i == 3)
            to = 999;

        multis[i] = new Multi_Thread();
        multis[i]->setItems(items, from, to);
        thrs[i] = new QThread();
        connect(thrs[i], SIGNAL(started()), multis[i], SLOT(bench2_2()));
        multis[i]->moveToThread(thrs[i]);
    }
    table->blockSignals(true);
    table->setUpdatesEnabled(false);
    table->setWordWrap(false);

    for(int i = 0; i < 4; i++)
    {
        if(!i)
            time.start();

        thrs[i]->start();
    }

    for(int i = 0; i < 4; i++)
    {
        thrs[i]->wait();
    }
    qDebug() << "\nbench2 3 " << time.elapsed();
    table->blockSignals(false);
    table->setUpdatesEnabled(true);
    table->setWordWrap(true);

    for(int i = 0; i < 4; i++)
    {
        delete multis[i];
        delete thrs[i];
    }
    delete[] multis;
    delete[] thrs;

    table->clear();
    for(int i = 0; i < 1000; i++)
    {
        delete[] items[i];
    }
    delete[] items;

}

multi_thread.h

#ifndef MULTI_THREAD_H
#define MULTI_THREAD_H

#include <QObject>
#include <QThread>
#include <QTableWidget>

class Multi_Thread : public QObject
{
    Q_OBJECT
public:
    explicit Multi_Thread();
    void setTable(QTableWidget *tab, int f, int t);
    void setItems(QTableWidgetItem ***i, int f, int t);

private:
    QTableWidget *table;
    QTableWidgetItem ***items;
    int from;
    int to;

signals:

public slots:
    void bench2_1();
    void bench2_2();

};

#endif // MULTI_THREAD_H

multi_thread.cpp

#include "multi_thread.h"

Multi_Thread::Multi_Thread() : QObject()
{
}

void Multi_Thread::setTable(QTableWidget *tab, int f, int t)
{
    table = tab;
    from = f;
    to = t;
}

void Multi_Thread::setItems(QTableWidgetItem ***i, int f, int t)
{
    items = i;
    from = f;
    to = t;
}

void Multi_Thread::bench2_1()
{
    for(int i = from; i <= to; i++)
    {
        for(int j = 0; j < 1000; j++)
        {
            table->item(i, j)->setText("0");
        }
    }
    QThread::currentThread()->exit(0);
}

void Multi_Thread::bench2_2()
{
    for(int i = from; i <= to; i++)
    {
        for(int j = 0; j < 1000; j++)
        {
            items[i][j]->setText("0");
        }
    }
    QThread::currentThread()->exit(0);
}

output.log

bench2 1  7654 
bench2 2  1160 
bench2 3  8021 

What is strange is that I was expecting "bench2 3" to be faster than "bench2 1".

PS: My laptop hardware can needs 4 threads to reach 100% usage. Please edit it as per your hardware requirements. Can be known from Environment variables.

解决方案

Calling table->item(...)->setText() from any thread other than the GUI thread is undefined behavior. Don't do it. It's nice that you're using a QObject in a QThread to do the job, but you must not directly call methods on objects that live in other threads.

The internal model will call the view's dataChanged() slot for each of your updates. This is the likely source of the slow-down, apart from the memory allocations for each item.

The typical way of preparing a data model in a separate thread would be to:

  1. Instantiate the model in a separate thread. The model must not yet be connected to any views at this point. Initialize the model with data. It can be a QStandardItemModel or a custom model. Custom models are not that hard, and can often be much more efficient if you don't need to perform a memory allocation per each item.

  2. Connect the model to the view (QTableView, not QTableWidget). You can move it to the gui thread for convenience, although you don't have to.

  3. Only access the model from the thread it lives in, unless you use queued method invocations or queued signal-slot connections. The view uses the latter and thus can live in a thread separate from the model.

A QTableWidget bundles an internal QStandardItemModel with a QTableView. If you want to deal with the model separately, you simply use the the model class in isolation from the view class.

这篇关于基准QTableWidget的多线程输入,奇怪的结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆