Qt下QThread用法学习之多线程

来源:互联网 发布:js返回上一个页面 编辑:程序博客网 时间:2024/06/01 08:24
本文参考了http://mobile.51cto.com/symbian-272733_1.htm和http://mobile.51cto.com/symbian-268690_1.htm两篇好文章,这个两篇文章各有不足,第一篇解释QThread线程工作的原理,但是没有具体直观的多线程实例,第二篇则给出了多线程服务器端建立的实例,但是多线程并没有真正的在不同的线程工作,本文依据第一篇博文,在第二篇的基础上修改,从debug的结果来看,实现了文件在新线程中运行,真正实现了多线程。代码如下:
多线程服务器端程序(完整的)

 

 

 

//tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include "tcpthread.h"
#include<QTcpServer>

 

class TcpServer:public QTcpServer
{
    Q_OBJECT
public:
    explicit TcpServer(QObject *parent=0);

    //Object obj;

signals://更新UI
    void bytesArrived(qint64,qint32,int);

protected:
    void incomingConnection(int socketDescriptor);
};

#endif

//tcpserver.cpp
#include "tcpserver.h"


TcpServer::TcpServer(QObject *parent):
    QTcpServer(parent)
{

}

//在incomingConnection中新建一个tcpsocket线程,并完成相应的信号连接。
void TcpServer::incomingConnection(int socketDescriptor)
{
    TcpThread *thread=new TcpThread(socketDescriptor,this);
qDebug()<<"server thread:"<<QThread::currentThreadId();    
    connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()));//关闭线程
    connect(thread,SIGNAL(bytesArrived(qint64,qint32,int)),this,SIGNAL(bytesArrived(qint64,qint32,int)));//接收数据
    
    thread->start();
}

#ifndef TCPTHREAD_H
#define TCPTHREAD_H

#include <QThread>
#include<QTcpSocket>
#include<QtNetwork>

class QFile;
class QTcpSocket;


class Object:public QObject {       
Q_OBJECT   
public: Object(int socketDescriptor);

public:
    int socketDescriptor;
    qint64 bytesReceived;
    qint64 byteToRead;
    qint32 TotalBytes;
    QTcpSocket *tcpSocket;
    QHostAddress fileNameIp;//文件名Ip部分
    QFile *localFile;
    QByteArray inBlock;//读取缓存
signals:
    void error(QTcpSocket::SocketError socketError);
    void bytesArrived(qint64,qint32,int);
    void receiveSgl(QTcpSocket*);

public slots:
    void receiveFile();
};  
class TcpThread:public QThread
{
    Q_OBJECT
public:
    TcpThread(int socketDescriptor,QObject *parent);
    ~TcpThread();
    void run();

public slots:
    //void receiveFile();
signals:
    void bytesArrived(qint64,qint32,int);
private:
    int socketDescriptor;
    

    //Object obj;
};

#endif

#include "tcpthread.h"
#include<QtGui>
#include<QtNetwork>

TcpThread::TcpThread(int socketDescriptor,QObject *parent):
    QThread(parent),socketDescriptor(socketDescriptor)
{
    //bytesReceived=0;
        
     //QObject::connect(this, SIGNAL(receiveSgl(QTcpSocket*)), &obj, SLOT(slot(QTcpSocket*)));  
}
Object::Object(int socketDescriptor):socketDescriptor(socketDescriptor)
{
    tcpSocket=new QTcpSocket;
    bytesReceived=0;
    if(!tcpSocket->setSocketDescriptor(socketDescriptor)){
        emit error(tcpSocket->error());
        return;
    }

    qDebug()<<socketDescriptor;
    QObject::connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(receiveFile()));      
}    

TcpThread::~TcpThread()
{
    quit();
    wait();
    deleteLater();
}
void TcpThread::run()
{

    Object obj2(socketDescriptor);
    obj2.moveToThread(this);  

    QObject::connect(&obj2,SIGNAL(bytesArrived(qint64,qint32,int)),this,SIGNAL(bytesArrived(qint64,qint32,int)),

                               Qt::BlockingQueuedConnection);//接收数据


//    qDebug()<<socketDescriptor;
////这是重中之重,必须加Qt::BlockingQueuedConnection!   
 //这里困扰了我好几天,原因就在与开始没加,默认用的Qt::AutoConnection。   
 //简单介绍一下QT信号与槽的连接方式:   
//Qt::AutoConnection表示系统自动选择相应的连接方式,如果信号与槽在同一线程,就采用Qt::DirectConnection,
//如果信号与槽不在同一线程,将采用Qt::QueuedConnection的连接方式。   
 //Qt::DirectConnection表示一旦信号产生,立即执行槽函数。   
 //Qt::QueuedConnection表示信号产生后,将发送Event给你的receiver所在的线程,postEvent(QEvent::MetaCall,...),
//slot函数会在receiver所在的线程的event loop中进行处理。   
 //Qt::BlockingQueuedConnection表示信号产生后调用sendEvent(QEvent::MetaCall,...),
//在receiver所在的线程处理完成后才会返回;只能当sender,receiver不在同一线程时才可以。   
 //Qt::UniqueConnection表示只有它不是一个重复连接,连接才会成功。如果之前已经有了一个链接(相同的信号连接到同一对象的同一个槽上),那么连接将会失败并将返回false。   
 //Qt::AutoCompatConnection与QT3保持兼容性   
 //说明一下,对于任何的QThread来说,其线程只存在于run()函数内,其它的函数都不在线程内,所以此处要采用Qt::BlockingQueuedConnection,   
 //因为当SOCKET有数据到达时就会发出readyRead()信号,但是此时可能之前的receiveFile()还未执行完毕,之前使用的Qt::AutoConnection,   
 //结果传输大文件的时候就会出错,原因就在于只要有数据到达的时候,就会连接信号,但是数据接收还没处理完毕,而Qt::BlockingQueuedConnection会阻塞   
 //此连接,直到receiveFile()处理完毕并返回后才发送信号。
qDebug()<<"run thread:"<<QThread::currentThreadId();
    //connect(tcpSocket,SIGNAL(readyRead()),this,SIGNAL(receiveSgl(tcpSocket)));
//    connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(receiveFile()),Qt::BlockingQueuedConnection);
    exec();
}



void Object::receiveFile()
{
qDebug()<<"obj thread:"<<QThread::currentThreadId();
    QDataStream in(tcpSocket);
    if(bytesReceived<sizeof(qint32))
    {
        if(tcpSocket->bytesAvailable()>=sizeof(qint32))
        {
            in.setByteOrder(QDataStream::LittleEndian);//linux系统是大端
            in>>TotalBytes;
            TotalBytes+=4;
            qDebug()<<TotalBytes;

            bytesReceived+=sizeof(qint32);
            fileNameIp = tcpSocket->peerAddress();
            quint16 port = tcpSocket->peerPort();
            localFile = new QFile(fileNameIp.toString()+(tr(".%1").arg(port))) ;
            if(!localFile->open(QFile::WriteOnly))
            {
    
            }
        }
    }
    if(bytesReceived<TotalBytes){
        byteToRead=tcpSocket->bytesAvailable();
        bytesReceived+=byteToRead;
        inBlock=tcpSocket->readAll();
        qDebug()<<"BytesReceived is :"<<bytesReceived;
        localFile->write(inBlock);
        inBlock.resize(0);
    }
    emit bytesArrived(bytesReceived,TotalBytes,socketDescriptor);
    if(bytesReceived==TotalBytes){
        localFile->close();
        qDebug()<<bytesReceived;
        //emit TcpThread::finished();
        QApplication::restoreOverrideCursor();
    }

}

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include "tcpthread.h"
#include "tcpserver.h"

class QDialogButtonBox;
class QTcpSokcet;
class QProgressBar;
class QLabel;
class QPushButton;
class QTextBrowser;
class Widget:public QWidget
{
    Q_OBJECT
public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
private:
    TcpServer tcpServer;
    QProgressBar *progressBar;
    QPushButton *okBtn;
    QPushButton *quitBtn;
    QLabel *statuslabel;
    QTextBrowser *textBrowser;
private slots:
    void okBtnClicked();
    void updateProgress(qint64,qint32,int);
};

#endif

#include "widget.h"
#include <QtNetwork>
#include <QtGui>
#include<QProgressBar>
#include<QTextBrowser>
Widget::Widget(QWidget *parent):QWidget(parent)
{
    progressBar=new QProgressBar;
    progressBar->setMaximum(2);
    progressBar->setValue(0);

    okBtn=new QPushButton("open");
    quitBtn=new QPushButton("quit");
    textBrowser=new QTextBrowser;
    statuslabel=new QLabel(this);
qDebug()<<"main thread:"<<QThread::currentThreadId();

    connect(okBtn,SIGNAL(clicked()),this,SLOT(okBtnClicked()));
    QGridLayout *mainLayout=new QGridLayout;
    mainLayout->addWidget(okBtn,0,0,1,1,0);
    mainLayout->addWidget(quitBtn,0,1,1,1,0);
    mainLayout->addWidget(textBrowser,1,0,5,2,0);
    mainLayout->addWidget(progressBar,6,0,1,2,0);
    mainLayout->addWidget(statuslabel,7,0,1,1,0);
    setLayout(mainLayout);
}

Widget::~Widget()
{

}

void Widget::okBtnClicked()
{
    okBtn->setEnabled(false);
    QApplication::setOverrideCursor(Qt::WaitCursor);

    while(!tcpServer.isListening()&&!tcpServer.listen(QHostAddress::Any,9001))
    {
        QMessageBox::StandardButton ret = QMessageBox::critical(this,tr("回环"),
        tr("无法开始测试:%1.").arg(tcpServer.errorString()),QMessageBox::Retry|QMessageBox::Cancel);
        if(ret==QMessageBox::Cancel)
        return;
    }

    statuslabel->setText(tr("监听端口:%1").arg("12345"));  
    connect(&tcpServer,SIGNAL(bytesArrived(qint64,qint32,int)),  
    this,SLOT(updateProgress(qint64,qint32,int)));  
 }  

void Widget::updateProgress(qint64 bytesReceived, qint32 TotalBytes, int socketDescriptor)  
{  
    progressBar->setMaximum(TotalBytes);  
    progressBar->setValue(bytesReceived);  
    statuslabel->setText(tr("已接收 %1MB").arg(bytesReceived)); /// (1024 * 1024)));  
    textBrowser->setText(tr("现在连接的socket描述符:%1").arg(socketDescriptor));  
}

#include "widget.h"
#include <QApplication>
#include<QtGui>


int main(int argc,char **argv)
{
    //QTranslator oTranslator;
    /*QFile file("qt_zh_CN.qm");
    bool b = file.exists();
    if(b)
        oTranslator.load("qt_zh_CN.qm");
    else
        qDebug()<<"Error\n";
    */
    
    //oTranslator.load("imagewindow_la");    

    QApplication app(argc,argv);
    //app.installTranslator(&oTranslator);

    QTextCodec::setCodecForTr(QTextCodec::codecForName("System"));
    QTextCodec::setCodecForLocale(QTextCodec::codecForName("System"));
    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("System"));

    Widget imageWin;
    imageWin.setFont(QFont("wqy-zenhei",14,QFont::Normal));
    imageWin.resize(400,300);
    imageWin.show();
    return app.exec();
}