Qt学习之路——Asynchronous QTcpServer with QThreadPool

September 13th, 2013 by JasonLe's Tech Leave a reply »

上一节,我们学习了QTcpSocket using multiple thread ,但是我们都知道Server一般都要维持大量的connections,导致Server需要频繁的申请分配内存等资源,如果我们使用ThreadPool,一次性分配一定量的资源,这样就避免了频繁的资源申请销毁。
使用ThreadPool,我们通过查询API,要使用start()函数
void QThreadPool::start ( QRunnable * runnable, int priority = 0 )
Reserves a thread and uses it to run runnable, unless this thread will make the current thread count exceed maxThreadCount(). In that case, runnable is added to a run queue instead. The priority argument can be used to control the run queue’s order of execution.

我们的任务类要继承QRunnable,这里要声明的是QRunnable基类不是QOBject类型。它与QThread的run类似,都要实现。
void QRunnable::run () [pure virtual]
Implement this pure virtual function in your subclass.

Note that the thread pool takes ownership of the runnable if runnable->autoDelete() returns true, and the runnable will be deleted automatically by the thread pool after the runnable->run() returns. If runnable->autoDelete() returns false, ownership of runnable remains with the caller. Note that changing the auto-deletion on runnable after calling this functions results in undefined behavior.
返回过来,我们来说threadpool,如果这个线程执行完毕,那么我们可以调用autoDelete()来让pool自动销毁。

首先我们要在构造函数中设置最大连接数,setMaxThreadCount(),还有各种SLOT函数。
QThreadPool::globalIntance()->setMaxThreadCount(20);

    connect(socket,SIGNAL(connected()),this,SLOT(connected()));
    connect(socket,SIGNAL(disconnected()),this,SLOT(disconnected()));
    connect(socket,SIGNAL(readyRead()),this,SLOT(readyRead()));

    socket->setSocketDescriptor(Descriptor);
    qDebug() << Descriptor <<"client connected";
}
void Myclient::connected()
{
    qDebug() << "client connected event";
}
void Myclient::disconnected()
{
    qDebug() << "client disconnected event";
}
void Myclient::readyRead()
{
    qDebug() << socket->readAll() ;

    MyTask *mytask = new MyTask();
    mytask->setAutoDelete(true);
    connect(mytask,SIGNAL(Result(int)),this,SLOT(TaskResult(int)),Qt::QueuedConnection);
    QThreadPool::globalInstance()->start(mytask);//完成 Asynchronous 调用

}
void Myclient::TaskResult(int Number)
{
    QByteArray Buffer;
    Buffer.append("\r\nTask Result =  ");
    Buffer.append(QString::number(Number));

    socket->write(Buffer);
}

这里要说明一下因为我们在线程池中另开了一个线程执行Task,所以,这个Task向Myclient发消息,属于跨线程发送消息,所以使用Qt::QueuedConnection,如果是同一个线程内的消息发送,直接Qt::DirectConnection就好。这个很重要!!!!!!

其中MyTask *mytask = new MyTask();是QRunnable类型,除此之外要使用Qt的信号与connect(),所以也要多重继承QObjec,并加入Q_OBJECT宏。这样才能引入signals与slots。

QRunnable 不继承自 QObject,故要在其中实现信号插槽,需要自行继承QObject,并添加Q_OBJCECT宏。 由此产生一个比较麻烦的事情:QRunnable线程中抛出的信号,用缺省的connectionType无法触发slot。经我的反复测试,使用Qt::DirectConnection可以触发slot,但要注意这时slot是在线程中执行的。这里有个小技巧,在slot中再次抛出信号,则其它用缺省方式connect这个信号的slot就又回到了原来的(slot本来所在的)线程。 

class MyTask : public QObject , public QRunnable
{
    Q_OBJECT
public:
    MyTask();

protected:
    void run();
signals:
    void Result(int Number);

};

然后我们在这个run()中实现我们的Task函数,这样我们就实现了Asynchronous QTcpServer。我们可以再Task中完成各种费时操作。