2

I have a very difficult time of understanding how to make a simplest possible working multithreaded Qt console application.

I have read tons of stuff on how to use QThread class. Some of them say subclass QThread, others say use worker class wrapper for QThread.

After several tries and retries I could still not make a working multithreaded Qt console application.

Right now I don't need any fancy Qt Gui.

Can someone please help me to fill the threading parts of the example code ? It just reads one line at the time from text file and the idea is that each thread (I want to use 4 threads) that is not busy at the moment will print that line to stdout with std::cout as soon as possible. Just print it, no other fancy processing stuff for now to keep this simple for me.

#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();
}
user2753972
  • 21
  • 1
  • 3
  • 1
    In multithreading applications, you won't use global variables. This is not safe. – fiscblog Sep 06 '13 at 11:12
  • Why you want it to be multithreaded anyway? What's your goal? Definitely it does not matter whether its a GUI app or not. – Janick Bernet Sep 06 '13 at 11:12
  • Well, my ultimate goal is to make a simple console network client application with multithreaded waitForConnected() TcpSocket calls. But for now I just need to know how to do any multithreading with simples possible example code. And yes, those waitForConnected() calls need each a different hostname that I would be reading from text file in a main thread – user2753972 Sep 06 '13 at 11:26
  • Provide some code, where you tried to do some multithreading. Using std::cout from different threads is bad idea. – Dmitry Sazonov Sep 06 '13 at 11:29
  • @fiscblog Then how are different threads communicating and passing state information if processing shared resource (text file in this case). I know nothing at all of multithreading – user2753972 Sep 06 '13 at 11:31
  • Don't use `waitForConnected` but the asynchronous signal-based methods. `waitForConnected` is horribly slow. – Janick Bernet Sep 06 '13 at 11:31
  • @user2753972: Use qt signals/slots to pass around state in multithreaded applications. Have a look at this: http://qt-project.org/doc/qt-4.8/threads-qobject.html – Janick Bernet Sep 06 '13 at 11:32
  • @DmitrySazonov: Why is it a bad idea ? – user2753972 Sep 06 '13 at 11:44
  • @inflagranti: but can I use normal connect signal/slot stuff in a console application ? And what if the connection needs to block ? For example. if using smtp protocol then surely it would be simpler to user waitForConnected() call in each thread ??? – user2753972 Sep 06 '13 at 11:48
  • @user2753972 cout is not thread safe in C++03 – Dmitry Sazonov Sep 06 '13 at 11:57
  • @user2753972 You can use signals and slots in a console application. – thuga Sep 06 '13 at 12:00
  • @DmitrySazonov: I would like to provide the code for my actual multithreaded attempts but Im not sure if posting that kind of code is legal here? You see, it's a naive implementation of login bruteforcer against smtp server, similar to THC hydra program that is available as opensource code. It works singlethreaded well but when trying to convert it to multithreaded things start to go hell. Also, as a side note, THC hydra does not work against gmail – user2753972 Sep 06 '13 at 12:12
  • @user2753972: Signal/Slots have again nothing to do with console application. And no, there is no reason do write blocking IO for SMTP neither, it might just be easier. But in general, you will get into hard limits using threads and blocking IO. If it allows you to write code much more easily, go for it though, but keep in mind that you might have to rewrite it later (especially, since again, the blocking socket operations in Qt are slow). – Janick Bernet Sep 06 '13 at 13:05
  • @inflagranti Oh, ok. I try to do that with traditional connect() stuff then and maybe try also that QtConcurrent and see witch one is simpler and executes faster – user2753972 Sep 06 '13 at 13:29
  • It's not about blocking I/O being slow somehow due Qt. It's unsubstiated nonsense. It's about blocking I/O having an overhead of one thread per connection. You don't write code like that whether you use Qt or not! On a recent Xeon-based server, using Qt, I can saturate our 50 Mbit/s pipe using just one thread running a couple hundred connections. Spreading it across a couple hundred threads does only one thing: a process that normally took 30% of one core now uses 100% of 3 cores due to context switching overhead. In a datacenter, that's real money lost. – Kuba hasn't forgotten Monica Sep 09 '13 at 11:18

4 Answers4

2

Putting the Functionality in a Slot in a QObject

The key points are:

  1. Remember that each QObject has a certain thread() that it "lives" in. Each thread can have an event loop running there. This event loop will deliver the events sent to the objects that "live" in this thread.

  2. Do not derive from QThread. Start stock QThreads. They'll start an even event loop in the default implementation of QThread::run().

  3. Implement your functionality in a slot (or a Q_INVOKABLE) method. The class obviously has to derive from QObject.

  4. The magic happens when you send signals (using signal-slot connection, not directly) to the slot in #3. The connection from the notifier, running in the GUI thread, to the notified objects is done automatically using the Qt::QueuedConnection since the sender and the receiver objects live in different threads.

    Sending a signal to such an object results in posting an event to the event queue of the thread the object is in. The event loop's event dispatcher will pick those events and call the appropriate slots. This is the power of Qt - a lot of useful stuff can be done for you.

Note that there is no notion of a "currently busy" thread. The threads execute short slots of the objects that live there. If you want to move threads between a "busy" and "not busy" states, then you'd need extra code for that.

Another way of implementing it would be to derive from QRunnable and use QThreadPool. That's in another answer.

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"
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
1

For your purposes I would not use QThread at all but the classes from QtConcurrent.

Something simple like (assuming you have C++11):

  while(!file.atEnd()) {

   QString line = file.readLine(); 

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

Though I'm still not sure what this should give you on a high level.

Janick Bernet
  • 20,544
  • 2
  • 29
  • 55
  • Is it stricly C++11 ? What version of Qt/QtCreator/gcc (whatever) I need for that ? – user2753972 Sep 06 '13 at 12:01
  • It's in there since Qt 4.7 at least. The C++11 part there is the lambda expression, but you can also do it with a named function, so C++11 is not necessary, but much more readable for such things. – Janick Bernet Sep 06 '13 at 13:03
  • One more question if I may. How many threads does that QtConcurrent::run() create in that while loop ? I mean, is there some internal limit or something ? Otherwise, if Im going to read 10 000 lines from text file then I feel a little uncomfortable if the program is also creating 10 000 threads :) – user2753972 Sep 06 '13 at 13:26
  • It uses underneath a QThreadPool which by default will use the number of processors as the thread count. – Janick Bernet Sep 06 '13 at 13:35
1

Below link can be useful for you for the information related to using threads in Qt

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

If you only want file reading to be done in Asynchronous ways Qt is having several alternate techniques like QtConcurrent.

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

Here is some example code to help you for using QtConcurrent

Running a Function in a Separate Thread

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

aFunction should contain the code for reading the file .

You can return the read data in the following way

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

Note that the QFuture::result() function blocks and waits for the result to become available. Use QFutureWatcher to get notification when the function has finished execution and the result is available.

Hope this helps. All the above code is taken from Qt documentation.

user1764879
  • 112
  • 1
  • 9
  • `I have read tons of stuff on how to use QThread class`, he said. – fiscblog Sep 06 '13 at 11:19
  • @user1764879: yes I have read that before and also http://woboq.com/blog/qthread-you-were-not-doing-so-wrong.html and also http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong but still unsure how to actually *use* them correctly. Either I am starting them wrong way or my wrapper class is totally wrong. and that qtconcurrent doc has no example code whatsoever :( – user2753972 Sep 06 '13 at 11:58
  • @user1764879: Ok. Thank you. I think your example combined with the answer inflagranti gave previously is going to push me into right direction. So I will just do the processing (printing of lines to stdout in this case) in some callback function that QtConcurrent:run() is calling in a while loop and that pretty much is it ? – user2753972 Sep 06 '13 at 13:24
  • need not use while loop ..pass the function name as argument to run function and return the data read from the file as string(it can be any other type also like QList ). – user1764879 Sep 09 '13 at 09:55
1

Putting the functionality in a QRunnable

Perhaps the solution that's closest to your explicit needs would use a QThreadPool. It does what you want: it picks a non-busy thread from its pool, and runs the worker there. If there are no free threads, it'll add the runnable to a run queue that gets drained each time a free thread becomes available.

Note, though, that your explicit wish of having a thread state, namely busy/non-busy, does not really mesh at all with a network penetration system that needs to wait for a reply before trying each new password. You'll want it based on QObjects. I'll modify my other answer to show how you might do it while managing network connections. It's very, very wasteful to waste threads on busy waiting for network answers. You do not want to do that. It will perform poorly.

Your application is I/O bound and could, pretty much, run on one thread without much in the way of undue performance loss. Only if you have a huge network pipe and are testing tens of thousands of accounts at the same time would you need more than one thread. I'm serious.

#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();
}
Community
  • 1
  • 1
Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Hmmm... This QRunnable seems simple enough, but are those threads started immediately ? I mean, if I read for example 10 000 line file then I want to start those threads as soon as possible and not after all 10 000 lines are read. I could even have a 7 million line text file so I can't wait it for read all and then start threads – user2753972 Sep 07 '13 at 12:09
  • The threads are started as soon as you invoke `pool->start()`. Of course the number of threads is limited, see the `QThreadPool` documentation. You do not want more threads than 2xnumber of cores anyway. `QThreadPool` takes care of that. – Kuba hasn't forgotten Monica Sep 07 '13 at 13:47
  • Ok thank you very much. What if I want to do some TcpSocket connections in that Worker class too ? How would I handle for example connect(socket,SIGNAL(connected()),this,SLOT(doSomeStuffWhenConnected())) ? – user2753972 Sep 07 '13 at 15:59
  • What if you wanted to handle connections in that Worker thread? Well, you probably wouldn't want to, since it wouldn't give you any advantages. Iterating (bruce forcing) passwords is entirely I/O bound. There's no computation to speak of. It can all run in one thread unless you have an enormous network connection, or run on a server in a datacenter. If you really don't know how to deal with signals and slots, you do need to read up on Qt tutorials. See my other answers for dozens of examples. Networking stuff too. – Kuba hasn't forgotten Monica Sep 09 '13 at 11:23