如何使用多线程创建一个简单的Qt控制台应用程序?

时间:2013-09-06 11:08:26

标签: c++ multithreading qt console

我很难理解如何使最简单的工作 多线程Qt 控制台应用程序。

我已经阅读了很多关于如何使用QThread类的内容。 其中一些人说子类QThread,其他人说使用QThread的工人类包装器。

经过多次尝试和重试后,我仍然无法使用多线程 Qt控制台应用程序。

现在我不需要任何花哨的Qt Gui。

有人可以帮我填写示例代码的线程部分吗? 它只是从文本文件中读取一行,并且想法是每个线程(我想使用4个线程)此刻不忙,将尽快使用std :: cout将该行打印到stdout。只需打印它,暂时没有其他花哨的处理东西来保持这个简单。

#include <QCoreApplication>
#include <QFile>
#include <iostream>

/* QThread stuff here */
/* Don't know how to make it */

int main(int argc, char *argv[])
{
        QCoreApplication a(argc, argv);

        /* Create four instances of threads here and 
           put them to wait readed lines */

    QFile   file("file.txt");
    file.open(QIODevice::ReadOnly | QIODevice::Text);

    while(!file.atEnd()) {
    /* Read lines here but where should they be saved?
       Into a global variable like QList<QByteArray> list ?
       So that each thread can read them from there or where ???? */

       ??? = file.readLine(); 
    }
    file.close();
    a.exit();
}

4 个答案:

答案 0 :(得分:2)

将功能放在QObject的插槽中

关键点是:

  1. 请记住,每个QObject都有一个{居住'的thread()。每个线程都可以在那里运行一个事件循环。此事件循环将传递发送到此线程中“活动”的对象的事件。

  2. 不要来自QThread。开始库存QThreads。他们将在QThread::run()的默认实现中启动偶数事件循环。

  3. 在广告位(或Q_INVOKABLE)方法中实施您的功能。该课程显然必须来自QObject

  4. 当您向#3中的插槽发送信号(使用信号 - 插槽连接,而不是直接)时,会发生魔力。从GUI线程中运行的通知程序到通知对象的连接是使用Qt::QueuedConnection自动完成的,因为发送方和接收方对象位于不同的线程中。

    向此类对象发送信号会导致将事件发布到对象所在线程的事件队列中。事件循环的事件调度程序将选择这些事件并调用相应的插槽。这是Qt的力量 - 可以为你做很多有用的事情。

  5. 请注意,没有“当前忙”线程的概念。线程执行那里的对象的短槽。如果你想在“忙”和“不忙”状态之间移动线程,那么你需要额外的代码。

    实现它的另一种方法是从QRunnable派生并使用QThreadPool。这是另一个答案。

    <强>的main.cpp

    #include <QCoreApplication>
    #include <QTextStream>
    #include <QThread>
    #include <QFile>
    #include <cstdio>
    
    class Notified : public QObject {
        Q_OBJECT
        QTextStream m_out;
    public:
        Q_SLOT void notify(const QString & text) {
            m_out << "(" << this << ") " << text << endl;
        }
        Notified(QObject *parent = 0) : QObject(parent), m_out(stdout) {}
    };
    
    class Notifier : public QObject {
        Q_OBJECT
        Q_SIGNAL void notification(const QString &);
    public:
        Notifier(QObject *parent = 0) : QObject(parent) {}
        void notifyLines(const QString & filePath) {
            QFile file(filePath);
            file.open(QIODevice::ReadOnly | QIODevice::Text);
            while (! file.atEnd()) {
                emit notification(file.readLine());
            }
            file.close();
        }
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
        QObjectList notifieds;
        QList<QThread*> threads;
        Notifier notifier;
    
        for (int i = 0; i < 4; ++i) {
            QThread * thread = new QThread(&a); // thread owned by the application object
            Notified * notified = new Notified; // can't have an owner before it's moved to another thread
            notified->moveToThread(thread);
            thread->start();
            notifieds << notified;
            threads << thread;
            notified->connect(&notifier, SIGNAL(notification(QString)), SLOT(notify(QString)));
        }
    
        notifier.notifyLines("file.txt");
    
        foreach (QThread *thread, threads) {
            thread->quit();
            thread->wait();
        }
        foreach (QObject *notified, notifieds) delete notified;
    
        a.exit();
    }
    
    #include "main.moc"
    

答案 1 :(得分:1)

出于您的目的,除QThread的课程外,我不会使用QtConcurrent

简单的事情(假设你有C ++ 11):

  while(!file.atEnd()) {

   QString line = file.readLine(); 

   QtConcurrent::run([line] 
       { 
         qDebug() << line; 
       }); 
   }

虽然我仍然不确定这会给你带来什么样的高水平。

答案 2 :(得分:1)

以下链接对于您在Qt

中使用线程相关的信息非常有用

http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

如果您只想以异步方式完成文件读取Qt有几种替代技术,如QtConcurrent。

http://qt-project.org/doc/qt-4.8/threads-qtconcurrent.html

以下是一些帮助您使用QtConcurrent

的示例代码

在单独的线程中运行函数

 extern QString aFunction();
 QFuture<void> future = QtConcurrent::run(aFunction);

aFunction应该包含用于读取文件的代码。

您可以按以下方式返回读取数据

QFuture<QString> future = QtConcurrent::run(aFunction);
 ...
QString result = future.result();

请注意,QFuture :: result()函数会阻止并等待结果可用。当函数执行完毕并且结果可用时,使用QFutureWatcher获取通知。

希望这会有所帮助。以上所有代码均来自Qt文档。

答案 3 :(得分:1)

将功能放在QRunnable

最接近显式需求的解决方案可能会使用QThreadPool。它做你想要的:它从池中选择一个非繁忙的线程,并在那里运行工作者。如果没有空闲线程,它会将runnable添加到每次有空闲线程可用时耗尽的运行队列。

但请注意,您明确希望拥有一个线程状态,即忙/非忙,并不真正与网络渗透系统完全匹配,需要在尝试每个新密码之前等待回复。你会希望它基于QObjects。我将修改我的其他答案,以显示在管理网络连接时如何执行此操作。在繁忙的等待网络答案时浪费线程是非常非常浪费的。你想要这样做。它表现不佳。

您的应用程序受I / O约束,并且几乎可以在一个线程上运行,而不会造成过度的性能损失。只有当您拥有庞大的网络管道并且同时测试数万个帐户时,您才需要多个线程。我很认真。

#include <QCoreApplication>
#include <QTextStream>
#include <QRunnable>
#include <QThreadPool>
#include <QFile>
#include <cstdio>

class Data : public QString {
public:
    Data(const QString & str) : QString(str) {}
};

class Worker : public QRunnable {
    QTextStream m_out;
    Data m_data;
public:
    void run() {
        // Let's pretend we do something serious with our data here
        m_out << "(" << this << ") " << m_data << endl;
    }
    Worker(const Data & data) : m_out(stdout), m_data(data) {}
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QThreadPool * pool = QThreadPool::globalInstance();

    QFile file("file.txt");
    file.open(QIODevice::ReadOnly | QIODevice::Text);
    while (! file.atEnd()) {
        const Data data(file.readLine());
        Worker * worker = new Worker(data);
        pool->start(worker);
    }
    file.close();
    pool->waitForDone();
}