- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我不确定问题的标题是否表述正确,因此为了解释我的真正意思,请考虑以下示例:
我创建了一个QApplication
和一个QWidget
,上面有一个QPushButton
。然后,我将一个处理程序附加到来自按钮的单击信号,如下所示:
void MyWidget::on_pushButton_clicked(){
//Never return
while(true);
}
最后,我启动应用程序的事件循环,当我运行程序并且窗口显示时,单击按钮。
就我而言,这将导致整个应用程序停止运行。所以我现在的问题是如何从代码中“检测”我的应用程序中发生了这种挂起?
我知道编写不在信号处理程序中返回的代码是不好的做法,我问这个问题是因为我想检测错误并从中恢复,可能通过完全重新启动应用程序来提高生产时的弹性。
谢谢!
最佳答案
要检测挂起的事件循环,您需要包装可以挂起事件循环的操作,以检测何时花费太长时间。 “操作”是QCoreApplication::notify
成员。调用它是为了将事件传递到所有线程中的所有事件循环。当处理事件的代码花费太长时间时,就会发生事件循环挂起。
当为给定线程输入notify
时,您可以记下进入时间。然后,在专用线程中运行的扫描器可以迭代此类时间的列表,并拾取卡住时间过长的线程。
下面的示例也用直方图说明了这一点。具有事件循环且卡住时间超过超时阈值的线程将以红色突出显示。我还说明了如何将 View 模型包装在数据源周围。需要 Qt 5 和 C++11 编译器。
注意:运行时警告QBasicTimer::stop:失败。可能尝试从不同的线程停止
并不是真正的问题。它们是一个 Qt 错误,仅具有表面意义,在这种特殊情况下可以忽略。您可以解决它们 - 请参阅 this question .
// https://github.com/KubaO/stackoverflown/tree/master/questions/eventloop-hang-25038829
#include <QtWidgets>
#include <QtConcurrent>
#include <random>
std::default_random_engine reng;
int ilog2(qint64 val) {
Q_ASSERT(val >= 0);
int ret = -1;
while (val != 0) { val >>= 1; ret++; }
return ret;
}
/// The value binned to contain at most \a binaryDigits significant digits.
/// The less significant digits are reset to zero.
qint64 binned(qint64 value, int binaryDigits)
{
Q_ASSERT(binaryDigits > 0);
qint64 mask = -1;
int clrBits = ilog2(value) - binaryDigits;
if (clrBits > 0) mask <<= clrBits;
return value & mask;
}
/// A safely destructible thread for perusal by QObjects.
class Thread final : public QThread {
Q_OBJECT
void run() override {
connect(QAbstractEventDispatcher::instance(this),
&QAbstractEventDispatcher::aboutToBlock,
this, &Thread::aboutToBlock);
QThread::run();
}
QAtomicInt inDestructor;
public:
using QThread::QThread;
/// Take an object and prevent timer resource leaks when the object is about
/// to become threadless.
void takeObject(QObject *obj) {
// Work around to prevent
// QBasicTimer::stop: Failed. Possibly trying to stop from a different thread
static constexpr char kRegistered[] = "__ThreadRegistered";
static constexpr char kMoved[] = "__Moved";
if (!obj->property(kRegistered).isValid()) {
QObject::connect(this, &Thread::finished, obj, [this, obj]{
if (!inDestructor.load() || obj->thread() != this)
return;
// The object is about to become threadless
Q_ASSERT(obj->thread() == QThread::currentThread());
obj->setProperty(kMoved, true);
obj->moveToThread(this->thread());
}, Qt::DirectConnection);
QObject::connect(this, &QObject::destroyed, obj, [obj]{
if (!obj->thread()) {
obj->moveToThread(QThread::currentThread());
obj->setProperty(kRegistered, {});
}
else if (obj->thread() == QThread::currentThread() && obj->property(kMoved).isValid()) {
obj->setProperty(kMoved, {});
QCoreApplication::sendPostedEvents(obj, QEvent::MetaCall);
}
else if (obj->thread()->eventDispatcher())
QTimer::singleShot(0, obj, [obj]{ obj->setProperty(kRegistered, {}); });
}, Qt::DirectConnection);
obj->setProperty(kRegistered, true);
}
obj->moveToThread(this);
}
~Thread() override {
inDestructor.store(1);
requestInterruption();
quit();
wait();
}
Q_SIGNAL void aboutToBlock();
};
/// An application that monitors event loops in all threads.
class MonitoringApp : public QApplication {
Q_OBJECT
Q_PROPERTY(int timeout READ timeout WRITE setTimeout MEMBER m_timeout)
Q_PROPERTY(int updatePeriod READ updatePeriod WRITE setUpdatePeriod MEMBER m_updatePeriod)
public:
using Histogram = QMap<qint64, uint>;
using Base = QApplication;
private:
struct ThreadData {
/// A saturating, binned histogram of event handling durations for given thread.
Histogram histogram;
/// Number of milliseconds between the epoch and when the event handler on this thread
/// was entered, or zero if no event handler is running.
qint64 ping = 0;
/// Number of milliseconds between the epoch and when the last histogram update for
/// this thread was broadcast
qint64 update = 0;
/// Whether the thread's event loop is considered stuck at the moment
bool stuck = false;
/// Whether the thread is newly detected
bool newThread = true;
};
using Threads = QMap<QThread*, ThreadData>;
QMutex m_mutex;
Threads m_threads;
int m_timeout = 1000;
int m_updatePeriod = 250;
class StuckEventLoopNotifier : public QObject {
MonitoringApp *m_app;
QBasicTimer m_timer;
struct State { QThread *thread; qint64 elapsed; };
QVector<State> m_toEmit;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
int timeout = m_app->m_timeout;
auto now = QDateTime::currentMSecsSinceEpoch();
m_toEmit.clear();
QMutexLocker lock(&m_app->m_mutex);
for (auto it = m_app->m_threads.begin(); it != m_app->m_threads.end(); ++it) {
if (it->ping == 0) continue;
qint64 elapsed = now - it->ping;
it->stuck = elapsed > timeout;
m_toEmit.push_back({it.key(), it->stuck ? elapsed : 0});
}
lock.unlock();
for (auto &sig : qAsConst(m_toEmit)) emit m_app->loopStateChanged(sig.thread, sig.elapsed);
}
public:
explicit StuckEventLoopNotifier(MonitoringApp * app) : m_app(app) {
m_timer.start(100, Qt::CoarseTimer, this);
}
};
StuckEventLoopNotifier m_notifier{this};
Thread m_notifierThread;
void threadFinishedSlot() {
auto const thread = qobject_cast<QThread*>(QObject::sender());
QMutexLocker lock(&m_mutex);
auto it = m_threads.find(thread);
if (it == m_threads.end()) return;
auto const histogram(it->histogram);
bool stuck = it->stuck;
m_threads.erase(it);
lock.unlock();
emit newHistogram(thread, histogram);
if (stuck) emit loopStateChanged(thread, 0);
emit threadFinished(thread);
}
Q_SIGNAL void newThreadSignal(QThread*, const QString &);
protected:
bool notify(QObject * receiver, QEvent * event) override {
auto const curThread = QThread::currentThread();
QElapsedTimer timer;
auto now = QDateTime::currentMSecsSinceEpoch();
QMutexLocker lock(&m_mutex);
auto &thread = m_threads[curThread];
thread.ping = now;
bool newThread = false;
std::swap(newThread, thread.newThread);
lock.unlock();
if (newThread) {
connect(curThread, &QThread::finished, this, &MonitoringApp::threadFinishedSlot);
struct Event : QEvent {
QThread *thread;
QPointer<MonitoringApp> app;
explicit Event(QThread *thread, MonitoringApp *app) :
QEvent(QEvent::None), thread(thread), app(app) {}
~Event() override {
// objectName() can only be invoked from the object's thread
emit app->newThreadSignal(thread, thread->objectName());
}
};
QCoreApplication::postEvent(curThread, new Event(curThread, this));
}
timer.start();
auto result = Base::notify(receiver, event); // This is where the event loop can get "stuck".
auto duration = binned(timer.elapsed(), 3);
now += duration;
lock.relock();
if (thread.histogram[duration] < std::numeric_limits<Histogram::mapped_type>::max())
++thread.histogram[duration];
thread.ping = 0;
qint64 sinceLastUpdate = now - thread.update;
if (sinceLastUpdate >= m_updatePeriod) {
auto const histogram = thread.histogram;
thread.update = now;
lock.unlock();
emit newHistogram(curThread, histogram);
}
return result;
}
public:
explicit MonitoringApp(int & argc, char ** argv);
/// The event loop for a given thread has gotten stuck, or unstuck.
/** A zero elapsed time indicates that the loop is not stuck. The signal will be
* emitted periodically with increasing values of `elapsed` for a given thread as long
* as the loop is stuck. The thread might not exist when this notification is received. */
Q_SIGNAL void loopStateChanged(QThread *, int elapsed);
/// The first event was received in a newly started thread's event loop.
/** The thread might not exist when this notification is received. */
Q_SIGNAL void newThread(QThread *, const QString & threadName);
/// The thread has a new histogram available.
/** This signal is not sent more often than each updatePeriod().
* The thread might not exist when this notification is received. */
Q_SIGNAL void newHistogram(QThread *, const MonitoringApp::Histogram &);
/// The thread has finished.
/** The thread might not exist when this notification is received. A newHistogram
* signal is always emitted prior to this signal's emission. */
Q_SIGNAL void threadFinished(QThread *);
/// The maximum number of milliseconds an event handler can run before the event loop
/// is considered stuck.
int timeout() const { return m_timeout; }
Q_SLOT void setTimeout(int timeout) { m_timeout = timeout; }
int updatePeriod() const { return m_updatePeriod; }
Q_SLOT void setUpdatePeriod(int updatePeriod) { m_updatePeriod = updatePeriod; }
};
Q_DECLARE_METATYPE(MonitoringApp::Histogram)
MonitoringApp::MonitoringApp(int &argc, char **argv) :
MonitoringApp::Base(argc, argv)
{
qRegisterMetaType<MonitoringApp::Histogram>();
connect(this, &MonitoringApp::newThreadSignal, this, &MonitoringApp::newThread,
Qt::QueuedConnection);
m_notifierThread.setObjectName("notifierThread");
m_notifierThread.takeObject(&m_notifier);
m_notifierThread.start();
}
QImage renderHistogram(const MonitoringApp::Histogram &h) {
const int blockX = 2, blockY = 2;
QImage img(1 + h.size() * blockX, 32 * blockY, QImage::Format_ARGB32_Premultiplied);
img.fill(Qt::white);
QPainter p(&img);
int x = 0;
for (auto it = h.begin(); it != h.end(); ++it) {
qreal key = it.key() > 0 ? log2(it.key()) : 0.0;
QBrush b = QColor::fromHsv(qRound(240.0*(1.0 - key/32.0)), 255, 255);
p.fillRect(QRectF(x, img.height(), blockX, -log2(it.value()) * blockY), b);
x += blockX;
}
return img;
}
class MonitoringViewModel : public QStandardItemModel {
Q_OBJECT
struct Item {
bool set = false;
QStandardItem *caption = 0, *histogram = 0;
void setCaption(QThread* thread, const QString &name) {
auto text = QStringLiteral("0x%1 \"%2\"").arg(std::intptr_t(thread), 0, 16).arg(name);
caption->setText(text);
}
};
QMap<QThread*, Item> m_threadItems;
Item &itemFor(QThread *thread, bool set = true) {
Item &item = m_threadItems[thread];
if (set && !item.set) {
item.caption = new QStandardItem;
item.histogram = new QStandardItem;
item.caption->setEditable(false);
item.histogram->setEditable(false);
int row = rowCount() ? 1 : 0;
insertRow(row);
setItem(row, 0, item.caption);
setItem(row, 1, item.histogram);
item.set = true;
newHistogram(thread, MonitoringApp::Histogram());
}
return item;
}
void newThread(QThread *thread, const QString &name) {
itemFor(thread).setCaption(thread, name);
}
void newHistogramImage(QThread *thread, const QImage &img) {
auto &item = itemFor(thread, false);
if (!item.set) return;
item.histogram->setSizeHint(img.size());
item.histogram->setData(img, Qt::DecorationRole);
}
Q_SIGNAL void newHistogramImageSignal(QThread *thread, const QImage &img);
void newHistogram(QThread *thread, const MonitoringApp::Histogram &histogram) {
QtConcurrent::run([this, thread, histogram]{
emit newHistogramImageSignal(thread, renderHistogram(histogram));
});
}
void loopStateChanged(QThread *thread, int elapsed) {
auto &item = itemFor(thread);
item.caption->setData(elapsed ? QColor(Qt::red) : QColor(Qt::transparent), Qt::BackgroundColorRole);
}
void threadFinished(QThread *thread) {
auto &item = itemFor(thread);
item.caption->setText(QStringLiteral("Finished %1").arg(item.caption->text()));
item.set = false;
}
public:
MonitoringViewModel(QObject *parent = 0) : QStandardItemModel(parent) {
connect(this, &MonitoringViewModel::newHistogramImageSignal,
this, &MonitoringViewModel::newHistogramImage);
auto app = qobject_cast<MonitoringApp*>(qApp);
connect(app, &MonitoringApp::newThread, this, &MonitoringViewModel::newThread);
connect(app, &MonitoringApp::newHistogram, this, &MonitoringViewModel::newHistogram);
connect(app, &MonitoringApp::threadFinished, this, &MonitoringViewModel::threadFinished);
connect(app, &MonitoringApp::loopStateChanged, this, &MonitoringViewModel::loopStateChanged);
}
};
class WorkerObject : public QObject {
Q_OBJECT
int m_trials = 2000;
double m_probability = 0.2;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) override {
if (ev->timerId() != m_timer.timerId()) return;
QThread::msleep(std::binomial_distribution<>(m_trials, m_probability)(reng));
}
public:
using QObject::QObject;
Q_SIGNAL void stopped();
Q_SLOT void start() { m_timer.start(0, this); }
Q_SLOT void stop() { m_timer.stop(); emit stopped(); }
int trials() const { return m_trials; }
void setTrials(int trials) { m_trials = trials; }
double probability() const { return m_probability; }
void setProbability(double p) { m_probability = p; }
};
int main(int argc, char *argv[])
{
MonitoringApp app(argc, argv);
MonitoringViewModel model;
WorkerObject workerObject;
Thread workerThread;
workerThread.setObjectName("workerThread");
QWidget w;
QGridLayout layout(&w);
QTableView view;
QLabel timeoutLabel;
QSlider timeout(Qt::Horizontal);
QGroupBox worker("Worker Thread");
worker.setCheckable(true);
worker.setChecked(false);
QGridLayout wLayout(&worker);
QLabel rangeLabel, probabilityLabel;
QSlider range(Qt::Horizontal), probability(Qt::Horizontal);
timeoutLabel.setMinimumWidth(50);
QObject::connect(&timeout, &QSlider::valueChanged, &timeoutLabel, (void(QLabel::*)(int))&QLabel::setNum);
timeout.setMinimum(50);
timeout.setMaximum(5000);
timeout.setValue(app.timeout());
view.setModel(&model);
view.verticalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
layout.addWidget(&view, 0, 0, 1, 3);
layout.addWidget(new QLabel("Timeout"), 1, 0);
layout.addWidget(&timeoutLabel, 1, 1);
layout.addWidget(&timeout, 1, 2);
layout.addWidget(&worker, 2, 0, 1, 3);
QObject::connect(&range, &QAbstractSlider::valueChanged, [&](int p){
rangeLabel.setText(QString("Range %1 ms").arg(p));
workerObject.setTrials(p);
});
QObject::connect(&probability, &QAbstractSlider::valueChanged, [&](int p){
double prob = p / (double)probability.maximum();
probabilityLabel.setText(QString("Probability %1").arg(prob, 0, 'g', 2));
workerObject.setProbability(prob);
});
range.setMaximum(10000);
range.setValue(workerObject.trials());
probability.setValue(workerObject.probability() * probability.maximum());
wLayout.addWidget(new QLabel("Sleep Time Binomial Distribution"), 0, 0, 1, 2);
wLayout.addWidget(&rangeLabel, 1, 0);
wLayout.addWidget(&range, 2, 0);
wLayout.addWidget(&probabilityLabel, 1, 1);
wLayout.addWidget(&probability, 2, 1);
QObject::connect(&workerObject, &WorkerObject::stopped, &workerThread, &Thread::quit);
QObject::connect(&worker, &QGroupBox::toggled, [&](bool run) {
if (run) {
workerThread.start();
QMetaObject::invokeMethod(&workerObject, "start");
} else
QMetaObject::invokeMethod(&workerObject, "stop");
});
QObject::connect(&timeout, &QAbstractSlider::valueChanged, &app, &MonitoringApp::setTimeout);
workerThread.takeObject(&workerObject);
w.show();
app.exec();
}
#include "main.moc"
关于qt - 如何检测 QEventLoop 中的挂起?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25038829/
我想做的是让 JTextPane 在 JPanel 中占用尽可能多的空间。对于我使用的 UpdateInfoPanel: public class UpdateInfoPanel extends JP
我在 JPanel 中有一个 JTextArea,我想将其与 JScrollPane 一起使用。我正在使用 GridBagLayout。当我运行它时,框架似乎为 JScrollPane 腾出了空间,但
我想在 xcode 中实现以下功能。 我有一个 View Controller 。在这个 UIViewController 中,我有一个 UITabBar。它们下面是一个 UIView。将 UITab
有谁知道Firebird 2.5有没有类似于SQL中“STUFF”函数的功能? 我有一个包含父用户记录的表,另一个表包含与父相关的子用户记录。我希望能够提取用户拥有的“ROLES”的逗号分隔字符串,而
我想使用 JSON 作为 mirth channel 的输入和输出,例如详细信息保存在数据库中或创建 HL7 消息。 简而言之,输入为 JSON 解析它并输出为任何格式。 最佳答案 var objec
通常我会使用 R 并执行 merge.by,但这个文件似乎太大了,部门中的任何一台计算机都无法处理它! (任何从事遗传学工作的人的附加信息)本质上,插补似乎删除了 snp ID 的 rs 数字,我只剩
我有一个以前可能被问过的问题,但我很难找到正确的描述。我希望有人能帮助我。 在下面的代码中,我设置了varprice,我想添加javascript变量accu_id以通过rails在我的数据库中查找记
我有一个简单的 SVG 文件,在 Firefox 中可以正常查看 - 它的一些包装文本使用 foreignObject 包含一些 HTML - 文本包装在 div 中:
所以我正在为学校编写一个 Ruby 程序,如果某个值是 1 或 3,则将 bool 值更改为 true,如果是 0 或 2,则更改为 false。由于我有 Java 背景,所以我认为这段代码应该有效:
我做了什么: 我在这些账户之间创建了 VPC 对等连接 互联网网关也连接到每个 VPC 还配置了路由表(以允许来自双方的流量) 情况1: 当这两个 VPC 在同一个账户中时,我成功测试了从另一个 La
我有一个名为 contacts 的表: user_id contact_id 10294 10295 10294 10293 10293 10294 102
我正在使用 Magento 中的新模板。为避免重复代码,我想为每个产品预览使用相同的子模板。 特别是我做了这样一个展示: $products = Mage::getModel('catalog/pro
“for”是否总是检查协议(protocol)中定义的每个函数中第一个参数的类型? 编辑(改写): 当协议(protocol)方法只有一个参数时,根据该单个参数的类型(直接或任意)找到实现。当协议(p
我想从我的 PHP 代码中调用 JavaScript 函数。我通过使用以下方法实现了这一点: echo ' drawChart($id); '; 这工作正常,但我想从我的 PHP 代码中获取数据,我使
这个问题已经有答案了: Event binding on dynamically created elements? (23 个回答) 已关闭 5 年前。 我有一个动态表单,我想在其中附加一些其他 h
我正在尝试找到一种解决方案,以在 componentDidMount 中的映射项上使用 setState。 我正在使用 GraphQL连同 Gatsby返回许多 data 项目,但要求在特定的 pat
我在 ScrollView 中有一个 View 。只要用户按住该 View ,我想每 80 毫秒调用一次方法。这是我已经实现的: final Runnable vibrate = new Runnab
我用 jni 开发了一个 android 应用程序。我在 GetStringUTFChars 的 dvmDecodeIndirectRef 中得到了一个 dvmabort。我只中止了一次。 为什么会这
当我到达我的 Activity 时,我调用 FragmentPagerAdapter 来处理我的不同选项卡。在我的一个选项卡中,我想显示一个 RecyclerView,但他从未出现过,有了断点,我看到
当我按下 Activity 中的按钮时,会弹出一个 DialogFragment。在对话框 fragment 中,有一个看起来像普通 ListView 的 RecyclerView。 我想要的行为是当
我是一名优秀的程序员,十分优秀!