Использование сигналов / слотов Qt вместо рабочего потока - PullRequest
2 голосов
/ 01 апреля 2010

Я использую Qt и хочу написать класс, который будет выполнять некоторые операции сетевого типа, подобные FTP / HTTP. Класс должен подключаться к множеству машин, один за другим, но мне нужно, чтобы пользовательский интерфейс приложений оставался (относительно) отзывчивым во время этого процесса, чтобы пользователь мог отменить операцию, выйти из приложения и т. Д. Первой мыслью было использовать отдельный поток для сетевых вещей, но встроенные классы Qt FTP / HTTP (и другие), очевидно, избегают использования потоков и вместо этого полагаются на сигналы и слоты. Итак, я хотел бы сделать что-то подобное и надеялся, что смогу сделать что-то вроде этого:

class Foo : public QObject
{
  Q_OBJECT
public:
  void start();

signals:
  void next();

private slots:
  void nextJob();
};

void Foo::start()
{
  ...
  connect(this, SIGNAL(next()), this, SLOT(nextJob()));
  emit next();
}

void Foo::nextJob()
{
  // Process next 'chunk'

  if (workLeftToDo)
  {
    emit next();
  }
}


void Bar::StartOperation()
{
   Foo* foo = new Foo;
   foo->start();
}

Однако это не работает, и пользовательский интерфейс останавливается, пока все операции не будут завершены. Я надеялся, что испускаемые сигналы на самом деле не вызовут слоты сразу, а будут как-то поставлены в очередь Qt, что позволит основному интерфейсу все еще работать.

Итак, что мне нужно сделать, чтобы заставить эту работу? Как Qt достигает этого с множеством встроенных классов, которые, кажется, выполняют длинные задачи в одном потоке?

Ответы [ 2 ]

4 голосов
/ 01 апреля 2010

Если вы выполняете длинную работу в потоке пользовательского интерфейса, пользовательский интерфейс будет зависать. Один из способов избежать этого - периодически звонить QCoreApplication::processEvents().

Вы должны быть ОЧЕНЬ осторожны, чтобы понять, что это делает, прежде чем вы решите это сделать. Вызов этой функции означает, что событие GUI может произойти в середине вашей операции. Если это событие может, в свою очередь, создать еще несколько заданий, вы можете в конечном итоге начать новое задание, находясь в середине старого задания.

Я бы не стал так быстро отказываться от подхода с использованием рабочих потоков. Преимущество состоит в том, что вы полностью отделяете работу от графического интерфейса пользователя, так что вы уверены, что начавшееся закончится.
Вы также должны учитывать, что Windows особенно иногда вводит нетривиальные задержки в циклы GUI. Если хост несколько занят или находится в состоянии переполнения памяти, вы обнаружите, что события графического интерфейса пользователя могут занять несколько долгих секунд, чтобы завершить и вернуть управление вашей обработке.

1 голос
/ 01 апреля 2010

Используйте QThread с методом запуска следующим образом:

void run(){ exec(); }

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

Примечание: убедитесь, что вы действительно используете цикл выполнения потока, добавив

moveToThread(this);

в конце Конструктора вашего класса Thread, производного от QThread (док не говорит об этом много)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...