[Development] Evolving Qt's multithreading API

Sze Howe Koh szehowe.koh at gmail.com
Fri Feb 22 11:57:29 CET 2013


On Feb 22, 2013 12:33 AM, "Olivier Goffart" <olivier at woboq.com> wrote:
> Some more common use case would be (pseudo-code)
>
> auto watcher = new QFutureWatcher;
> QObject::connect(watcher, SIGNAL(finished()), myObject, SLOT(doStuff()));
> watcher->setFuture(QThrerad::run([]() { return computeStuff(); } ));
> // who deletes the watcher?

If the caller creates the watcher, I think it's fine to let the caller
delete it too. It's an ordinary C++ practice.


> I think this pattern should be made easier
> Something like:
>
> QObject::connect(
> QThread::runFunction([]() { return doSomethingComplicated(); }),
> &QThread::finished,
> this,
> &MyObject::doStuff //cannot use lambda here because we want a
> // QueuedConnection
> );

I like this simple pattern. We might have some technical issues to
address though; see below.


> > I leaned towards returning QThread* because QtConcurrent::run()
> > couldn't use most of QFuture's features anyway, and QThread* can be
> > returned from all 3 static functions:
> >
> > static QThread* QThread::setupSimpleThread(QRunnable *runnable);
> > static QThread* QThread::setupSimpleThread(Function func, Args arg,
> > ...); static QThread* QThread::setupEventLoop(QObject* worker);
>
> So then the caller is responsible on calling start() and also deleting the
> thread ?

Yes, that was my original plan. Someone complained that they couldn't
bind a function + arguments to QtConcurrent::run() first, then run it
at a later time. This approach gives them the opportunity to start it
whenever they want.

On second thought, I'm not sure if this would be commonly needed. We
can make it start immediately instead, BUT this requires a guarantee
that the first thread can always connect the finished() signal, before
the new thread runs and finishes. Can that be guaranteed? If not,
we'll need:

    QThread *thread = QThread::runFunction([]() { return
doSomethingComplicated(); });
    connect(thread, &QThread::finished, this, &MyObject::doStuff);
    thread->start(); // Manual start, after all connections have been made

OR

    QThread *thread = QThread::runFunction([]() { return
doSomethingComplicated(); });
    thread->wait(); // Wait for auto-started thread to end
    doStuff();

The latter is like Corentin's approach using QFuture. Which is better?
(Personally I think the latter defeats the purpose of having a 2nd
thread)

Regarding deletion, we can follow the example of QAudioInput::start()
-- it returns a pointer to a QIODevice for reading, but retains
ownership of the device and auto-deletes it when stopped. Automatic
deletion is needed to prevent a memory leak in the nice simple pattern
you wrote above.


> > > The implementation use the same sort of generator that QtConcurrent.
> > > Thiago suggested making this feature only compatible C++11, which
> > > would make it easier to maintain. I actually envisaged to send a mail
> > > about that particular issue.
> > > Can c++03 really be dropped for that particular feature ?
> > > Also, I we were to make a c++11 only feature, what would be the
> > > benefits over std::async ?
> >
> > I would love to see an implementation using C++11. I think that
> > decision can't be made lightly though, and should be a separate
> > discussion -- it has implications for all other Qt modules.
>
> What do you mean?
> Do you mean an implementation of QThread that would use std::thread as an
> internal rather than pthread/windows API? (qthread_cxx11.cpp)
>
> I think it would be good to have. But would just be another layer, and more
> code to maintain.

I meant variadic templates and std::bind. QtConcurrent::run() uses a
code generator to produce 6 separate templates. In theory, we can
replace all that with 1 variadic template. No code generator, fewer
templates, less maintenance.

    template <typename TFunc, typename... TArgs>
    static QThread* setupSimpleThread(TFunc&& func, TArgs&&... args)
    {
        // Private class, derived from QThread
        return new QFunctionThread(
                std::bind(std::forward<TFunction>(a_func),
                        std::forward<TArgs>(a_args)...));
    }

We do have many older compilers to support still. Is Qt ready to start
introducing features that require C++11? Are we in a position to drive
its adoption, as Thiago put it?
(http://www.macieira.org/blog/2011/09/cxx11-support-in-qt-5/)


Regards,
Sze-Howe



More information about the Development mailing list