我遇到的问题是我决定按照他们应该的方式实施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
线程更快或更慢。
答案 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::sleep
,QThread::msleep
和QThread::usleep
已公开,因此您可以直接调用它们。在早期的Qt版本中,它们是static protected
。
e.g。 QThread::sleep(5); // sleep for 5 seconds