QThreads , QObject and sleep function

六眼飞鱼酱① 提交于 2019-12-18 03:41:58

问题


The problem I encountered is that I decided to implement QThreads the way they are supposed to, based on numerous articles:
http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

and issue at hand is that since the algorithm is run in separate QObject (wrapped in QThread) . how can I call out something like Thread::Sleep or smth .. Any ideas ?

A small description of the software. Basically my application solves TSP ( Traveling salesman problem). As the search goes along, it saves all the states in the history as frames ..(like visual frames). The search algorithms will be run on one thread . Main thread is handling with the GUI. Then there is the Mediaplayer like thread which tells Main thread what frame to display on screen. So where does the sleep come in ? In gui there is a slider that user can use to fast forward or go in normal pace.. that slider tells via signal slot to Mediaplayer thread to go faster or slower.


回答1:


What we've done is basically something like this: (written by memory, as I don't have our code checked out on this computer)

class Sleeper : public QThread {
public:
   void sleep(int ms) { QThread::sleep(ms); }
};

void sleep(int ms);

// in a .cpp file:
static Sleeper slp;

void sleep(int ms) {
    slp.sleep(ms);
}

The key is that the QThread::sleep function causes the calling thread to sleep, not the threaf represented by the QThread instance. So just create a wrapper which calls it via a custom QThread subclass.

Unfortunately, QThread is a mess. The documentation tells you to use it incorrectly. A few blog posts, as you've found, tell you a better way to do it, but then you can't call functions like sleep, which should never have been a protected thread member in the first place.

And best of all, even no matter which way you use QThread, it's designed to emulate what's probably the worst thread API ever conceived of, the Java one. Compared to something sane, like boost::thread, or even better, std::thread, it's bloated, overcomplicated and needlessly hard to use and requiring a staggering amount of boilerplate code.

This is really one of the places where the Qt team blew it. Big time.




回答2:


The simple answer: you're not supposed to block in asynchronous, run-to-completion code -- every event handler and slot implementation in a QObject is supposed to do its job and return, as soon as possible. It's not supposed to do any sort of busy waiting or sleeping. For more ranting along this line, see Miro Samek's I hate RTOSes.

For a much better implementation that follows from the above, see this answer instead. Macro trickery that follows below is best left to the poor souls stuck with C.

I've attached an example of how to do it the right way at least from the point of view of what the code does. If you want a real implementation, look no farther than Boost's stackless coroutines.

The macro trickery is syntactic sugar - it makes the technique more palatable (Boost does it better than I do below). Whether you use macros or write out the methods explicitly, is up to you. The syntax is not what is claimed to be the "right way" of doing it. I'm not the only one to use such preprocessor trickery. Missing is support nested function calls, and multiple "threads" of run-to-completion execution within a QObject. The example shows code for only one "thread" and only one level of async function calls. Stackless Python takes this to the logical conclusion.

You'll see this pattern in all of your code if you write it in an asynchronous way. The SLEEP macro is syntax sugar to help make the code easier to follow. There's no truly clean way to write it without a hacky macro in C++ where the syntax wouldn't be overbearing. Even as of C++11, the language has no built-in support for yield. See Why wasn't yield added to C++0x?.

This is truly non-blocking code, you'll see that the periodic timer event fires while you're "asleep". Do note that this cooperative multitasking has a much lower overhead than thread/process switches done by the OS. There's a reason why 16 bit Windows application code was written this way: it performs quite well, even on meager hardware.

Note that this code does not need a QThread, and in fact doesn't use a QThread, although if you'd move the object to a high priority thread, the delays will have lower spread.

The Qt timer implementation is clever enough to decrease the timer tick period on Windows, if the period is "short". You can use the platform-specific code I show below, but it should be discouraged. On Qt 5, you'd simply start a Qt::PreciseTimer timer. Do note that on pre-Windows 8 systems you're trading off power consumption and a slightly higher kernel overhead for performance here. Windows 8, OS X (xnu) and modern Linux are tickless and don't suffer from such performance degradation.

I should acknowledge the clear preprocessor abuse direction from Creating C macro with ## and __LINE__ (token concatenation with positioning macro).

Similarly to the SLEEP() macro, you can also implement a GOTO() macro, to allow you having simple finite state machines that are written in an easier-to-follow blocking code style, yet are asynchronous behind the scenes. You can have ENTER() and LEAVE() macros to implement actions to be done on state entry and exit, etc, yet the code can look entirely like a straight-coded blocking-style function. I've found it quite productive, and easier to follow than code that lacks any syntactic sugarcoating. YMMV. In the end, you would have something that's on the way to UML statecharts, but with less overhead (both runtime and code-text-wise) than QStateMachine-based implementations.

Below is the output, the asterisks are periodic timer ticks.

doing something
*
*
*
*
*
*
*
*
*
*
slept, a=10
*
*
*
*
*
slept, a=20
*
*
slept, a=30
*
slept, a=40
#sleep.pro
QT       += core
QT       -= gui
TARGET = sleep
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
//main.cpp
#ifdef Q_WS_WIN
#include <windows.h>
#endif
#include <cstdio>
#include <QtCore/QTextStream>
#include <QtCore/QObject>
#include <QtCore/QBasicTimer>
#include <QtCore/QTimer>
#include <QtCore/QCoreApplication>

QTextStream out(stdout);

// this order is important
#define TOKENPASTE2(x,y) x ## y
#define TOKENPASTE(x,y) TOKENPASTE2(x,y)
#define SLEEP(ms) sleep(ms, &SLEEPCLASS::TOKENPASTE(fun, __LINE__)); } void TOKENPASTE(fun, __LINE__)() {

class Object : public QObject
{
    Q_OBJECT
    #define SLEEPCLASS Object // used by the SLEEP macro
public:
    Object() {
        QTimer::singleShot(0, this, SLOT(slot1()));
        periodic.start(100);
        connect(&periodic, SIGNAL(timeout()), SLOT(tick()));
    }
protected slots:
    void slot1() {
        a = 10; // use member variables, not locals
        out << "doing something" << endl;
        sleep(1000, &Object::fun1);
    }
    void tick() {
        out << "*" << endl;
    }

protected:
    void fun1() {
        out << "slept, a=" << a << endl;
        a = 20;
        SLEEP(500);
        out << "slept, a=" << a << endl;
        a = 30;
        SLEEP(250);
        out << "slept, a=" << a << endl;
        a = 40;
        SLEEP(100);
        out << "slept, a=" << a << endl;
        qApp->exit();
    }

private:
    int a; // used in place of automatic variables

private:
    void sleep(int ms, void (Object::*target)()) {
        next = target;
        timer.start(ms, this);
    }
    void timerEvent(QTimerEvent * ev)
    {
        if (ev->timerId() == timer.timerId()) {
            timer.stop(); (this->*next)();
        }
    }
    QTimer periodic;
    QBasicTimer timer;
    void (Object::* next)();
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    Object o1;
#ifdef Q_WS_WIN
   timeBeginPeriod(1); // timers will be accurate to 1ms
#endif
    return a.exec();
}

#include "main.moc"



回答3:


I agree with jalf. I have a thread that acts as a sort of DBUS daemon and needs to listen to messages forever. Two things to mention:

jalf has

void sleep(int ms) { QThread::sleep(ms); }

But this is NOT MILLISECONDS! QThread::sleep() takes seconds. Also, if one is to take this approach, he must also include the QThread lib anyway, so it might be easier to just make the call like this:

QThread::sleep(seconds);

directly in the code. That way there isn't an extra header file. I ran this and it also works as jalf explained. (putting the calling thread to sleep.)




回答4:


For Qt 4.8.0 (the version I'm using), QThread::sleep, QThread::msleep and QThread::usleep have been made public so you can just call them directly. In earlier Qt versions, they were static protected.

e.g. QThread::sleep(5); // sleep for 5 seconds



来源:https://stackoverflow.com/questions/10776509/qthreads-qobject-and-sleep-function

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!