Почему QTimer::singleShot блокирует мой основной поток в момент времени 1, а не 0

Я отлаживал некоторый код, который, кажется, блокирует основной поток, когда графический интерфейс не виден. Я сократил его до следующего фрагмента кода и обнаружил, что проблема связана с моей реализацией QTimer::singleShot.

Чтобы воспроизвести проблему, я установил таймер на 1 (миллисекунда), чтобы запустить цикл приложения и отправить окно приложения в фоновый режим. В конце концов приложение остановится, пока пользовательский интерфейс не будет переведен на передний план.

Если я установлю таймер на 0, он будет работать безупречно, независимо от того, где находится главное окно приложения. Я без проблем увеличил размер цикла до 4 миллионов.

Я думаю, что я застрял в том, в чем разница между

QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));

и

QTimer::singleShot(0, this, SLOT(emptyListOfAnotherObjects()));?

Почему один блокирует основной поток над другим?

Для чего стоит запускать этот цикл в другом потоке, сохраняя отдельный графический интерфейс, приводит к той же проблеме.

class AnotherObject : public QObject
{
    Q_OBJECT

public:
    AnotherObject();

    void process();

signals:
    void done();
};

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

public slots:
    void start(bool);

private slots:
    void emptyListOfAnotherObjects();

    void delayEmptyOfAnotherObject();

private:
    QList<AnotherObject*> m_listOfAnotherObject;
    Ui::MainWindow *ui;
};

==

AnotherObject::AnotherObject()
{
    qDebug() << "CTOR AnotherObject" << this;
}

void AnotherObject::process()
{
    emit done();
    deleteLater();
}

//==

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    connect(ui->start, SIGNAL(clicked(bool)), this, SLOT(start(bool)));
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::start(bool)
{
    for(long i=0; i<40000; i++){
        m_listOfAnotherObject.append(new AnotherObject());
    }

    emptyListOfAnotherObjects();
}

void MainWindow::emptyListOfAnotherObjects()
{
    if(m_listOfAnotherObject.isEmpty()) {
        qDebug() << "List empty, done.";
        return;
    }

    AnotherObject* i = m_listOfAnotherObject.takeFirst();
    connect(i, SIGNAL(done()), this, SLOT(delayEmptyOfAnotherObject()));
    i->process();

    qDebug() << m_listOfAnotherObject.count();
}

void MainWindow::delayEmptyOfAnotherObject()
{
    QTimer::singleShot(1, this, SLOT(emptyListOfAnotherObjects()));
}

Спасибо

Обновление:

Извините, надо было добавить, что это Qt 5.2 на OSX.


person aManFromOuterSpace    schedule 04.04.2016    source источник
comment
Я попробовал ваш код и не могу воспроизвести вашу проблему. Все работает хорошо на 0 и 1 мс. Что вы подразумеваете под отправкой приложения в фоновый режим? Я пробовал много вещей, но, похоже, работает свободно.   -  person Paraboloid87    schedule 04.04.2016
comment
@aManFromOuterSpace — На какой платформе вы это тестируете? Если вы используете OS X, это может быть связано с приложение для сна   -  person TheDarkKnight    schedule 04.04.2016
comment
Как только главное окно выходит на передний план, я нажимаю кнопку, чтобы запустить цикл, затем нажимаю другое открытое окно, QtCreator в моем случае, и вывожу это приложение на передний план, а мое приложение уходит на задний план.   -  person aManFromOuterSpace    schedule 04.04.2016
comment
Я сделал то же самое. На Win10 с Qt 5.5.1, 64бит работает!   -  person Paraboloid87    schedule 04.04.2016
comment
@TheDarkKnight Qt 5.2 для OSX. Приложение для сна, никогда о таком не слышал. Звучит подозрительно, посмотрю внимательнее.   -  person aManFromOuterSpace    schedule 04.04.2016
comment
Как показано в документации, вы можете просто проверьте это, проверив в Activity Monitor, включив столбец App Nap, чтобы показать, какие процессы находятся в этом состоянии.   -  person TheDarkKnight    schedule 04.04.2016
comment
@TheDarkKnight App Nap, кажется, виноват. Я вижу, как он включается в мониторе активности в тот же момент, когда останавливается вывод в отладчике. Теперь обходной путь. Я обнаружил, что если я заменю QTimer::singleShot на QMetaObject::invokeMethod, он сможет пройти 4 миллиона циклов без проблем... странно.   -  person aManFromOuterSpace    schedule 04.04.2016
comment
Боковое примечание: использование одиночного выстрела в 1 мс очень расточительно, вы никогда не должны использовать его, если вам буквально не нужна задержка в 1 мс для какой-либо цели, например, для стимуляции вывода MIDI. Если вы хотите передать управление циклу обработки событий до тех пор, пока не закончатся доступные события, вместо этого используйте нулевой тайм-аут. Вам также не нужен файл .ui и т. д., чтобы воспроизвести это, вы действительно не минимизировали этот тестовый пример, насколько это возможно.   -  person Kuba hasn't forgotten Monica    schedule 04.04.2016


Ответы (1)


App Nap, вероятно, является причиной этого проблема.

Я подозреваю, что использование invokeMethod помещает сообщение в очередь событий потока, что предотвращает зависание приложения. Напротив, использование QTimer ожидает и проверяет время ожидания, поэтому пустая очередь событий позволит ему войти в состояние сна.

В любом случае можно отключить сон приложения из вашего приложения, как описано здесь.

Поскольку вы используете Qt, вам нужно использовать файл .mm для инкапсулировать код Objective-C.

person TheDarkKnight    schedule 04.04.2016