Skip navigation.
KDE Developer's Journals

A Simple Threading Example

rich's picture

A topic that I've not mentioned in any of my blog posts is threading, not because I have anything against it, simply because a simple use-case hadn't come up. Today I was coding something easy to describe, where using threads was a good solution, so let's take a look at it.

The problem I needed to solve was to calculate cryptographic hashes of files that could be very large - for example 4 gigabyte DVD isos. As with any graphical application it is very important to ensure that the GUI remains responsive while this work is done. Since the problem involves very little communication between the part of the application calculating the hash, and the rest of the code a worker thread is an ideal solution.

The code that actually does the work is very simple, it uses the handy QCryptographicHash class to do the work (stored in the variable hasher):

HasherThread::HasherThread(QObject *parent) :
    QThread(parent)
{
    hasher = new QCryptographicHash( QCryptographicHash::Sha1 );
}

void HasherThread::run()
{
    QFile f( filename );
    if ( !f.open(QIODevice::ReadOnly) ) {
        emit error( QString("Unable to open file %1").arg(filename) );
        return;
    }

    hasher->reset();

    char buffer[16*1024];
    qint64 count;
    do {
        count = f.read( buffer, sizeof(buffer) );
        if ( count == -1 ) {
            emit error( QString("Read error") );
            break;
        }
        hasher->addData( buffer, count );
    } while( !f.atEnd() );

    emit completed( hasher->result().toHex() );
}

The code opens the file to be hashed, then reads through it in 16K chunks which it passes to the hasher. If this code were executed in the application's gui thread then this would cause the interface to freeze until the hash was ready, but the code lives in a class that inherits QThread.

One important feature to note about the code above is that the communication with the rest of the application is via signals (the error and completed signals to be precise). This is very important as it means that when we use the class, we can simply use it like this:

    hasherThread = new HasherThread( this );
    connect( hasherThread, SIGNAL(error(const QString &)),
             ui->resultEdit, SLOT(setText(const QString &)) );
    connect( hasherThread, SIGNAL(completed(const QString &)),
             ui->resultEdit, SLOT(setText(const QString &)) );

The special thing in the code above, is that we didn't have to do anything special - Qt just solved our inter-thread communication issues for us. What I mean, it that if our code had simply tried to output the result by calling ui->resultEdit->setText() directly then we would be attempting to access the GUI from outside the GUI thread which would most likely cause our application to crash. Instead, since we're using the default type of connections 'AutoConnection', what this means is that Qt will spot if the thread in which a signal is emitted is different from the one in which it is received and handle the necessary synchronisation for us - nice!

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
bugmenot's picture

QtConcurrent

You could have used QtConcurrent::run() and QFuture.

krake's picture

Depends on your goal

QtConcurrent does not necessarily run the code in a different thread it can use the calling thread.

I had been using QtConcurrent::run() in the KResource Akonadi bridge plugins assuming that would execute the code given to run() in a separate thread, however several crash reports later with backtraces clearly indicating the opposite I changed to explicit threading to be safe.

rich's picture

Yes, that would work too. I

Yes, that would work too. I think it's probably overkill for a simple case like the above though.

bugmenot's picture

I find that weird. I think

I find that weird. I think it's overkill to create a new class when a simple function (and the help of QtConcurrent::run() and QFuture) would do the job just fine.

rich's picture

I can see where you're coming

I can see where you're coming from, but using QtConcurrent would introduce a whole new framework to what was a pretty simple example. It would also mean that the Qt build would require exceptions to be enabled. The concurrent framework is nice, but I wouldn't add a dependency to it for something as simple as the example I was covering.

panzi's picture

I always wondered how does Qt

I always wondered how does Qt detect this? Is there a field in every QObject that refers to the thread it's owned by?

rich's picture

Yes, that's right. You can

Yes, that's right. You can query the thread in which a QObject lives using QObject::thread().

krake's picture

Inter-Thread Connections

Aren't Queued Connections only used if the emitting object has been created in the context of a different thread or has been moved to a different thread?

If you create the object and connect within the same thread context (here that's probably the main thread), aren't both connects Direct?

vdboor's picture

Nice post

Nice post! I think this would be a nice example to put into techbase.

The only thing missing is how to start a thread.
Not hasherThread->run() but hasherThread->start() does the trick. Smiling

rich's picture

Good point, I should have

Good point, I should have mentioned that.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.