QThreads,QObject和睡眠功能

时间:2012-05-27 19:04:29

标签: c++ qt qthread

我遇到的问题是我决定按照他们应该的方式实施QThreads,基于众多文章:
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/

并且问题在于,因为算法是在单独的QObject中运行的(包含在QThread中)。我该怎么称呼Thread::Sleep或smth ......有什么想法吗?

软件的小描述。 基本上我的申请解决了TSP(旅行商问题)。随着搜索的进行,它将历史中的所有状态保存为frames ..(如可视框架)。搜索算法将在一个线程上运行。 主线程正在使用GUI进行处理。 然后有Mediaplayer之类的线程告诉Main线程在屏幕上显示什么帧。那么睡眠在哪里? 在gui中有一个滑块,用户可以使用它来快进或以正常速度前进。滑块通过信号槽告诉Mediaplayer线程更快或更慢。

4 个答案:

答案 0 :(得分:6)

我们所做的基本上是这样的:(由内存写,因为我没有在这台电脑上检查我们的代码)

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);
}

关键是QThread::sleep函数导致调用线程休眠,而不是QThread实例表示的threaf。因此,只需创建一个通过自定义QThread子类调用它的包装器。

不幸的是,QThread 一团糟。文档告诉您错误地使用它。正如您所发现的,一些博客文章告诉您更好的方法,但是您不能调用sleep之类的函数,这些函数在第一篇文章中永远不应该是受保护的线程成员放置

最重要的是,即使您使用QThread的哪种方式,它也可以模拟可能是最糟糕的线程API(Java)。与理智的东西相比,如boost::thread,甚至更好,std::thread,它臃肿,过于复杂,不必要地难以使用,并且需要大量的样板代码。

这真是Qt团队吹响它的地方之一。很开心。

答案 1 :(得分:4)

简单的答案:您不应该阻止异步,运行到完成代码 - QObject中的每个事件处理程序和插槽实现都应该完成其工作并返回,只要可能。它不应该做任何忙碌的等待或睡觉。有关此行的更多咆哮,请参阅Miro Samek的I hate RTOSes

为了更好地实现上述内容,请参阅this answer instead下面的宏观技巧最好留给陷入C语言的可怜人。

我已经附上了一个如何以正确的方式做到这一点的例子,至少从代码的作用来看。如果您想要真正的实现,请查看不超过Boost's stackless coroutines

宏技巧是语法糖 - 它使技术更加可口(Boost比我下面做的更好)。无论您是使用宏还是明确地写出方法,都取决于您。语法是而不是所谓的“正确方法”。我是not the only one to use such preprocessor trickery。缺少是支持嵌套函数调用,以及QObject内的运行到完成执行的多个“线程”。该示例仅显示一个“线程”的代码,并且仅显示一个级别的异步函数调用。 Stackless Python认为这是合乎逻辑的结论。

如果以异步方式编写代码,您将在所有代码中看到此模式。 SLEEP宏是语法糖,有助于使代码更容易遵循。在没有C ++的hacky宏的情况下编写它没有真正干净的方法,其中语法不会过于霸道。即使在C ++ 11中,该语言也没有对yield的内置支持。请参阅Why wasn't yield added to C++0x?

这是真正的非阻塞代码,您会看到定期计时器事件在您“睡着”时触发。请注意,此协作式多任务处理的开销比操作系统完成的线程/进程切换要低得多。这就是为什么16位Windows应用程序代码以这种方式编写的原因:即使在微薄的硬件上也能很好地运行。

请注意,此代码需要QThread,并且实际上不使用QThread,但是如果您将对象移动到高优先级线程,延迟将有较低的传播。

如果时间段“短”,Qt计时器实现足够聪明,可以减少Windows上的计时器滴答时间。您可以使用我在下面显示的特定于平台的代码,但不应该这样做。在Qt 5上,你只需启动一个Qt::PreciseTimer计时器。请注意,在Windows 8之前的系统上,您需要牺牲功耗和稍高的内核开销来提高性能。 Windows 8,OS X(xnu)和现代Linux都没有任何问题,并且不会受到这种性能下降的影响。

我应该承认来自Creating C macro with ## and __LINE__ (token concatenation with positioning macro)的明确的预处理程序滥用方向。

SLEEP()宏类似,您还可以实现一个GOTO()宏,以允许您使用以易于遵循的阻塞代码样式编写的简单有限状态机,但是它们是异步的在幕后。您可以使用ENTER()LEAVE()宏来实现对状态进入和退出等操作,但代码看起来完全像直接编码的阻塞式函数。我发现它比非缺乏任何语法糖衣的代码更有效率,更易于遵循。因人而异。最后,你会有一些东西在去UML状态图的路上,但是比QStateMachine-based实现更少的开销(运行时和代码文本)。

下面是输出,星号是周期性计时器滴答。

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"

答案 2 :(得分:3)

我同意贾尔夫的观点。我有一个线程,作为一种DBUS守护进程,需要永远听取消息。有两点需要注意:

jalf

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

但这不是MILLISECONDS! QThread :: sleep()需要几秒钟。另外,如果要采用这种方法,他还必须包含QThread lib,因此可能更容易进行这样的调用:

QThread::sleep(seconds);

直接在代码中。这样就没有额外的头文件了。我跑了这个,它也像jalf解释的那样工作。 (将调用线程置于休眠状态。)

答案 3 :(得分:0)

对于Qt 4.8.0(版本I&#39; m使用),QThread::sleepQThread::msleepQThread::usleep已公开,因此您可以直接调用它们。在早期的Qt版本中,它们是static protected

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

相关问题