2

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

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

经过几次尝试和重试,我仍然无法制作一个有效的多线程 Qt 控制台应用程序。

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

有人可以帮我填写示例代码的线程部分吗?它一次只从文本文件中读取一行,其想法是目前不忙的每个线程(我想使用 4 个线程)将尽快使用 std::cout 将该行打印到标准输出。只需打印它,现在没有其他花哨的处理东西来让我保持简单。

#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

4 回答 4

2

将功能放在 QObject 的插槽中

关键点是:

  1. 请记住,每个线程都有其“存在”QObject的确定性thread()。每个线程都可以在那里运行一个事件循环。这个事件循环将把发送的事件传递给在这个线程中“存活”的对象。

  2. 不要从QThread. 开始备货QThreads。他们将在QThread::run().

  3. Q_INVOKABLE在插槽(或)方法中实现您的功能。该类显然必须派生自QObject.

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

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

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

实现它的另一种方法是派生QRunnable和使用QThreadPool. 那是在另一个答案中。

主文件

#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"
于 2013-09-06T12:30:18.570 回答
1

将功能放入 QRunnable

也许最接近您的明确需求的解决方案将使用QThreadPool. 它做你想做的事:它从它的池中挑选一个不忙的线程,并在那里运行工人。如果没有空闲线程,它会将可运行线程添加到运行队列中,每次空闲线程可用时,该队列都会被耗尽。

但是请注意,您明确希望拥有一个线程状态,即忙碌/非忙碌,与需要在尝试每个新密码之前等待回复的网络渗透系统完全不匹配。你会想要它基于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();
}
于 2013-09-06T13:46:46.873 回答
1

出于您的目的QThread,除了QtConcurrent.

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

  while(!file.atEnd()) {

   QString line = file.readLine(); 

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

虽然我仍然不确定这应该给你带来什么。

于 2013-09-06T11:14:28.457 回答
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 文档。

于 2013-09-06T11:15:25.253 回答