我在一款Qt App中有一个后台线程用于收集数据。该线程应该在每次迭代之间睡眠100毫秒,但它并不总是正常工作。当该App为OS X最高级别的应用程序时,睡眠正常工作。但当它不是最高级别的应用程序时,睡眠的持续时间不确定,可能长达约10秒,在大约一分钟的操作后。
下面是一个简单的Cocoa App,演示了这个问题(注意.mm表示objc++)
AppDelegate.mm:
使用 std::thread 而非 QThread 时,行为相同(在代码中已注释掉)。
下面是一个简单的Cocoa App,演示了这个问题(注意.mm表示objc++)
AppDelegate.mm:
#import "AppDelegate.h"
#include <iostream>
#include <thread>
#include <libgen.h>
using namespace std::chrono;
#define DEFAULT_COLLECTOR_SAMPLING_FREQUENCY 10
namespace Helpers {
uint64_t time_ms() {
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
}
std::thread _collectorThread;
bool _running;
@interface AppDelegate ()
@end
@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_running = true;
uint64_t start = Helpers::time_ms();
_collectorThread =
std::thread (
[&]{
while(_running) {
uint64_t t1, t2;
t1 = Helpers::time_ms();
std::this_thread::sleep_for((std::chrono::duration<int, std::milli>)(1000 / DEFAULT_COLLECTOR_SAMPLING_FREQUENCY));
t2 = Helpers::time_ms();
std::cout << (int)((t1 - start)/1000) << " TestSleep: sleep lasted " << t2-t1 << " ms" << std::endl;
}
});
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
_running = false;
_collectorThread.join();
}
@end
标准输出:
0 TestSleep: sleep lasted 102 ms. // Window is in background
0 TestSleep: sleep lasted 101 ms. // behind Xcode window
0 TestSleep: sleep lasted 104 ms
0 TestSleep: sleep lasted 104 ms
0 TestSleep: sleep lasted 105 ms
0 TestSleep: sleep lasted 105 ms
0 TestSleep: sleep lasted 105 ms
0 TestSleep: sleep lasted 104 ms
0 TestSleep: sleep lasted 102 ms
0 TestSleep: sleep lasted 102 ms
1 TestSleep: sleep lasted 105 ms
1 TestSleep: sleep lasted 105 ms
1 TestSleep: sleep lasted 104 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 100 ms
...
...
52 TestSleep: sleep lasted 102 ms
52 TestSleep: sleep lasted 101 ms
52 TestSleep: sleep lasted 104 ms
52 TestSleep: sleep lasted 105 ms
52 TestSleep: sleep lasted 104 ms
52 TestSleep: sleep lasted 100 ms
52 TestSleep: sleep lasted 322 ms. // after ~1 minute,
53 TestSleep: sleep lasted 100 ms. // sleep gets way off
53 TestSleep: sleep lasted 499 ms
53 TestSleep: sleep lasted 1093 ms
54 TestSleep: sleep lasted 1086 ms
56 TestSleep: sleep lasted 1061 ms
57 TestSleep: sleep lasted 1090 ms
58 TestSleep: sleep lasted 1100 ms
59 TestSleep: sleep lasted 1099 ms
60 TestSleep: sleep lasted 1096 ms
61 TestSleep: sleep lasted 390 ms
61 TestSleep: sleep lasted 100 ms
61 TestSleep: sleep lasted 102 ms // click on app window
62 TestSleep: sleep lasted 102 ms // to bring it to foreground
62 TestSleep: sleep lasted 105 ms
另一方面,以下完整的程序不会减缓速度:
#include <iostream>
#include <thread>
#include <libgen.h>
using namespace std::chrono;
#define DEFAULT_COLLECTOR_SAMPLING_FREQUENCY 10
namespace Helpers {
uint64_t time_ms() {
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
}
int main(int argc, char *argv[])
{
bool _running = true;
uint64_t start = Helpers::time_ms();
std::thread collectorThread = std::thread (
[&]{
while(_running) {
uint64_t t1, t2;
t1 = Helpers::time_ms();
std::this_thread::sleep_for((std::chrono::duration<int, std::milli>)(1000 / DEFAULT_COLLECTOR_SAMPLING_FREQUENCY));
t2 = Helpers::time_ms();
std::cout << (int)((t1 - start)/1000) << " TestSleep: sleep lasted " << t2-t1 << " ms" << std::endl;
}
});
collectorThread.join();
return 0;
}
// clang++ -std=c++14 -o testc++ main.cpp
标准输出:
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 105 ms
0 TestSleep: sleep lasted 105 ms
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 104 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 104 ms
1 TestSleep: sleep lasted 102 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 100 ms
...
...
99 TestSleep: sleep lasted 101 ms
99 TestSleep: sleep lasted 105 ms
99 TestSleep: sleep lasted 104 ms
100 TestSleep: sleep lasted 104 ms
100 TestSleep: sleep lasted 101 ms
100 TestSleep: sleep lasted 104 ms
我的原始应用是QML,也显示出相同的减速行为。
TestSleep.pro:
QT += quick
CONFIG += c++11
SOURCES += \
main.cpp
RESOURCES += qml.qrc
main.qml:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Scroll")
ScrollView {
anchors.fill: parent
ListView {
width: parent.width
model: 20
delegate: ItemDelegate {
text: "Item " + (index + 1)
width: parent.width
}
}
}
}
main.cpp:
#define DEFAULT_COLLECTOR_SAMPLING_FREQUENCY 10
namespace Helpers {
uint64_t time_ms() {
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
}
int main(int argc, char *argv[])
{
bool _running = true;
QThread *collectorThread = QThread::create(
// std::thread collectorThread = std::thread (
[&]{
while(_running) {
uint64_t t1;
t1 = Helpers::time_ms();
QThread::msleep(1000 / DEFAULT_COLLECTOR_SAMPLING_FREQUENCY);
// std::this_thread::sleep_for((std::chrono::duration<int, std::milli>)(1000 / DEFAULT_COLLECTOR_SAMPLING_FREQUENCY));
t1 = Helpers::time_ms() - t1;
std::cout << "TestUSleep: sleep lasted " << t1 << " ms" << std::endl;
}
});
collectorThread->start();
collectorThread->setPriority(QThread::TimeCriticalPriority);
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine.rootObjects().isEmpty())
return -1;
int returnValue = app.exec();
// collectorThread.join();
collectorThread->quit();
collectorThread->wait();
collectorThread->deleteLater();
return returnValue;
}
标准输出:
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 102 ms
0 TestSleep: sleep lasted 100 ms
0 TestSleep: sleep lasted 102 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 101 ms
0 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 100 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 101 ms
1 TestSleep: sleep lasted 101 ms
...
...
63 TestSleep: sleep lasted 100 ms
63 TestSleep: sleep lasted 101 ms
63 TestSleep: sleep lasted 102 ms
63 TestSleep: sleep lasted 101 ms
63 TestSleep: sleep lasted 101 ms
63 TestSleep: sleep lasted 7069 ms # slows down
70 TestSleep: sleep lasted 235 ms
70 TestSleep: sleep lasted 10100 ms
80 TestSleep: sleep lasted 7350 ms
88 TestSleep: sleep lasted 10100 ms
98 TestSleep: sleep lasted 3566 ms
101 TestSleep: sleep lasted 100 ms
102 TestSleep: sleep lasted 3242 ms
105 TestSleep: sleep lasted 2373 ms
107 TestSleep: sleep lasted 100 ms # click on main window
107 TestSleep: sleep lasted 101 ms # to put app on top
107 TestSleep: sleep lasted 101 ms # and back to normal
107 TestSleep: sleep lasted 101 ms # behavior
108 TestSleep: sleep lasted 101 ms
108 TestSleep: sleep lasted 102 ms
...
使用 std::thread 而非 QThread 时,行为相同(在代码中已注释掉)。
setPriority
不起作用。 - zneaksetPriority
调用是无效的,因为在调用start
(它还接受一个可选的优先级参数)之前,它是一个空操作。 - zneak