QThread блокирует основное приложение

У меня есть простой пользовательский интерфейс формы, в котором есть слот для кнопки, запускающей поток:

void MainWindow::LoadImage()
{
    aThread->run();
}

И метод run() выглядит так:

void CameraThread::run()
{
    qDebug("Staring Thread");
    while(1)
    {
        qDebug("ping");
        QThread::sleep(1);
    }
}

Когда я нажимаю кнопку, которая вызывает LoadImage(), пользовательский интерфейс перестает отвечать на запросы. Я периодически вижу сообщение «ping» в качестве вывода отладки, но пользовательский интерфейс зависает, ни на что не реагирует. Почему мой поток не работает отдельно? CameraThread, полученный как общедоступный QThread. Я использую gcc версии 4.4.3 (Ubuntu 4.4.3-4ubuntu5) с библиотеками QT и QT Creator из репозиториев Ubuntu 10.04 (x86).


person Atilla Filiz    schedule 09.07.2010    source источник


Ответы (3)


Краткий ответ: запустите поток, вызвав aThread->start();, а не run(), и убедитесь, что метод run() вашего потока защищен (не общедоступен).

Пояснение

Вызов start() — это правильный способ запустить поток, поскольку он обеспечивает приоритет планирования и фактически выполняет метод run() в контексте собственного потока.

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

  1. QThread сам по себе не является потоком. Это просто оболочка вокруг потока, и это приводит нас к...
  2. Сигналы/слоты, определенные в классе CameraThread, не обязательно будут выполняться в контексте потока, помните, что только метод run() и вызываемые из него методы выполняются в отдельном потоке.

ИМХО, создание подкласса QThread в большинстве случаев - это не путь. Вы можете сделать это намного проще с помощью следующего кода, и это избавит вас от многих головных болей.

class ImageLoader : public QObject {
Q_OBJECT
public slots:
    void doWork() 
    {
        // do work
    }
};

void MainWindow::MainWindow(/*params*/) 
{
  ImageLoader loader;
  QThread thread;
  loader.moveToThread( &thread );
  connect( this, SIGNAL( loadImage() ), &loader ,SLOT( doWork() ) );
  thread.start();
  // other initialization
}
void MainWindow::LoadImage()
{
   emit loadImage();
}

Также прочитайте блог Qt по этой теме. .

person Casey    schedule 09.07.2010
comment
Документация QT объясняет использование QThread путем создания подклассов. Почему это плохая идея? - person Atilla Filiz; 09.07.2010
comment
@Atilla - если вы прочитаете ссылку на блог QT в ответе Кейси, вы найдете ее под заголовком «Вы делаете это неправильно…». Это дискуссия о том, почему документация Qt о создании подклассов QThread не является правильным способом создания потоков. Кроме того, если я правильно прочитал запись в блоге, она написана человеком, который написал исходную документацию Qt. - person photo_tom; 09.07.2010
comment
Да, это правильно. По сути, документация Qt вводит в заблуждение и, следовательно, заставляет многих людей делать хакерские вещи (например, movetoThread(this) в конструкторе Qthread), которые вызывают проблемы в будущем. Надеюсь, документы скоро будут обновлены. - person Casey; 09.07.2010
comment
@photo_tom, @Casey, спасибо за разъяснения. В моем случае мой отдельный поток не имеет слотов, предполагается, что он работает в пользовательском цикле (постоянно опрашивает оборудование), и ему нужно только время от времени информировать основное приложение. Итак, в моем случае я считаю, что подклассы - правильный путь. - person Atilla Filiz; 11.07.2010
comment
Я знаю, что это давно мертвый вопрос, но я должен не согласиться с подклассом объекта QThread как с правильным путем... Все равно создайте свой QObject и threadInstance.moveToThread(myObjInstance);, но просто имейте слот, который при вызове будет зацикливаться навсегда (или до тех пор, пока какое-то условие переменная изменяется из-за другого соединения sig/slot). Это имеет дополнительное преимущество, заключающееся в возможном использовании одного и того же точного потока для выполнения большего количества вещей, чем просто работа с одним объектом (вы можете использовать столько объектов, сколько хотите, в отдельный поток). Пока вы выполняете processEvents в своем цикле cont, все будет будь хорошим. - person g19fanatic; 14.12.2012
comment
Это просто плохой OO для подкласса объекта QThread, если вы не пытаетесь сделать что-то особенное с потоками, а НЕ просто пытаетесь запустить код в другом потоке, чем ваш поток графического интерфейса... - person g19fanatic; 14.12.2012

Вы должны вызвать thread->start() не run... run - это точка входа для потока. Тема запускается с start. Вы звоните напрямую, поэтому и блокируете свой гуй. Проверьте документацию QThread. virtual void QThread::run() защищен (не без причины)

person Kamil Klimek    schedule 09.07.2010

Я думаю, проблема может заключаться в том, что вы не вызываете QtCore.QThread._init__(self) в конструкторе. Я была такая же проблема. Также я думаю, что вы не должны переопределять функцию запуска, просто переопределяйте функцию run(). Это решило ту же проблему, что и у меня. Даже без каких-либо задержек sleep() окно должно реагировать.

person Abhishek    schedule 11.07.2010
comment
Нет, моя проблема заключалась в вызове run() вместо start(). Я уже переопределяю run(). - person Atilla Filiz; 11.07.2010