Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: demaker от Май 26, 2016, 11:35



Название: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 11:35
Простите, что задаю идиотские вопросы  :), но еще раз :)
1.
Код:
    moveToThread()

Хотелось уточнить можно ли делать так:

Код
C++ (Qt)
Thread::Thread(QObject *parent):
            QThread(parent)
{
   moveToThread(this);
}
 
void Thread::run()
{
 
  exec();
}
 

Если да, то получается что объект потока у нас перемещается в поток, который описывается в run.

2.В контексте какого потока будет обрабатываться

Код:
Thread::slot_time_out

, если не указывать Qt::ConnectionType ???
Если я правильно понимаю в потоке, в котором был создан объект класса Thread.
Т.е автоматически получается
Код:
Qt::QueueConnection
.
Если неправ то поправьте.

Код
C++ (Qt)
Thread::Thread(QObject*parent):
      QThread(parent)
{
 
}
 
void Thread::run()
{
  QTimer *timer = new QTimer();
 
  connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out()));
 
  timer->start(500);
  exec();
}
 
void Thread::slot_time_out()
{
//
}
 

можно ли делать так???

Код:
connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out()),Qt::DirectConnection);

Если, да то тогда
Код:
Thread::slot_time_out()
будет обрабатываться в контексте потока Thread  и
надо мьютексировать поля класса Thread, так как он создан в основном потоке(в другом).

Если я не прав то поправьте и сильно не пинайте, пожалуйста :)


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Igors от Май 26, 2016, 12:36
Здесь Вы напоролись на исключение: события таймера должны приниматься именно в том потоке где он создан (Thread), а значит надо добавить moveToThread(this) или, как рекомендует теория, сделать это извне.

"объект перемешается в поток", "в контексте потока" - такие мутно-неудачные термины/выражения капитально запутывают. В действительности все очень просто. Разберемся как срабатывает Qt::AutoConnection. Допустим мы создали какой-то коннект
Код:
QObject::connect(src, SIGNAL(itemChanged(int)), dst, SLOT(itemChanged(int))); // Qt::AutoConnection
При испускании сигнала текущая нитка сравнивается с dst->thread(). Если они равны - прямой вызов (DirectConnection). Если нет то  QueuedConnection, сигнал помещается в очередь EventLoop той нитки (dst->thread()), и слот будет вызван при извлечении этого события и выполнен в потоке thread(). Конечно этот EventLoop должен быть запущен (QThread::exec())

Конструктор QObject устанавливает начальное значение thread() на текущую нитку (где выполняется код конструктора). Ну и, как Вы уже догадались, именно пресловутый moveToThread пере-устанавливает тот самый указатель что возвращает thread(). Хз почему это называют довольно нелепо "перемещение"(?), или, еще лучше, "объект живет в потоке"  :)


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 13:06
Здесь Вы напоролись на исключение: события таймера должны приниматься именно в том потоке где он создан (Thread), а значит надо добавить moveToThread(this) или, как рекомендует теория, сделать это извне.
)

Что значит извне???

Есть книга "Qt4 на примерах" автор Юрий Земсков.
Есть пример кода Эхо-Сервера:
Код
C++ (Qt)
EchoThread::EchoThread(int socketDescriptor,QObject*parent = 0):
                QThread(parent),m_SocketDescriptor(socketDescriptor)
{
 
}
 
void EchoThread::run()
{
 m_client = new QTcpSocket();
 ...
 connect(m_client,SIGNAL(readyRead()),this,SLOT(onRead),Qt::DirectConnection);
 exec();
}
 
void EchoThread::onRead()
{
QByteArray block = m_client->readAll;
}
 

1. разве можно использовать здесь Qt::DirectConnection???
2. почему m_client не защищают мьютексами???

В приложенном файле скан странницы.


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Old от Май 26, 2016, 13:36
Есть книга "Qt4 на примерах" автор Юрий Земсков.
Это потому что Юра не знал как сделать правильно, поэтому он руками прописывал тип коннекта, что бы слоты вызывались в контексте нужной нитки. :)


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 13:44
Есть книга "Qt4 на примерах" автор Юрий Земсков.
Это потому что Юра не знал как сделать правильно, поэтому он руками прописывал тип коннекта, что бы слоты вызывались в контексте нужной нитки. :)

Т.е здесь нужен DirectConnection чтобы обрабатывать непосредственно слот в созданном потоке. Т.к объект нитки создан в основном. И если бы не было DirectConnection, то connect был бы автоматически Qt::QueueConnection. Так получается???

А если бы мы добавили
Код:
moveToThread(this)
 в конструктор EchoThread то DirectConnection можно было бы не писать. Я правильно Вас понимаю???

Да и в коде
Код
C++ (Qt)
Thread::Thread(QObject*parent):
      QThread(parent)
{
 
}
 
void Thread::run()
{
  QTimer *timer = new QTimer();
 
  connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out())); //тип Qt::DirectConnection указывать
                                                                                          //или не надо???  
 
  timer->start(500);
  exec();
}
 
void Thread::slot_time_out()
{
//
}
 



Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Old от Май 26, 2016, 14:02
Т.е здесь нужен DirectConnection чтобы обрабатывать непосредственно слот в созданном потоке. Т.к объект нитки создан в основном. И если бы не было DirectConnection, то connect был бы автоматически Qt::QueueConnection. Так получается???
Да. Объект класса QThread и объекты созданные в его методе run имеют разные контексты, поэтому был би применен тип QueueConnection.

А если бы мы добавили
Код:
moveToThread(this)
 в конструктор EchoThread то DirectConnection можно было бы не писать. Я правильно Вас понимаю???
Да.


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 14:08
Да и в коде
Код
C++ (Qt)
Thread::Thread(QObject*parent):
      QThread(parent)
{
 
}
 
void Thread::run()
{
  QTimer *timer = new QTimer();
 
  connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out())); //тип Qt::DirectConnection указывать
                                                                                          //или не надо???  
 
  timer->start(500);
  exec();
}
 
void Thread::slot_time_out()
{
//
}
 


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Old от Май 26, 2016, 14:19
Да и в коде
Если вы объект Thread перенесете в контекст нового потока через moveToThread( this ), то не надо указывать.
Контекст у объекта получателя (Thread) и отправителя (таймер) будут одинаковы и DirectConnection будет применяться по умолчанию.


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Igors от Май 26, 2016, 14:21
Код
C++ (Qt)
  connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out())); //тип Qt::DirectConnection указывать
                                                                                          //или не надо???  
 
Конкретно для сигнала таймера - без разницы, это не поможет если this->thread() != this, а иначе и так Direct


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 14:29
А moveToThread() это опасно или нет???

И еще, если в конструкторе Thread не делать
Код:
moveToThread(this)
и у класса есть поля, то их желательно мьютексировать, так как они могут использоваться и главном потоке. Правильно???


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Old от Май 26, 2016, 14:31
А moveToThread() это опасно или нет???
Вы изменяете одну переменную, значение которой нужно для определения типа коннекта. Не знаю насколько для вас это опасно. :)


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 14:37
Код
C++ (Qt)
  connection(timer,SIGNAL(timeOut()),this,SLOT(slot_time_out())); //тип Qt::DirectConnection указывать
                                                                                          //или не надо???  
 
Конкретно для сигнала таймера - без разницы, это не поможет если this->thread() != this, а иначе и так Direct

Ну да в том случае в котором я описал ниже this->thread() != this и если я хочу в этом потоке обрабатывать сигнал от таймера в этом потоке, то надо писать Qt::DirectConnection. Или нет???


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 14:49
А moveToThread() это опасно или нет???
Вы изменяете одну переменную, значение которой нужно для определения типа коннекта. Не знаю насколько для вас это опасно. :)

moveToThread изменяет thread() и тем самым объект перемещается в другой поток. Так???


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: Igors от Май 26, 2016, 15:04
moveToThread изменяет thread() и тем самым объект перемещается в другой поток. Так???
Никто никуда не "перемешается", меняется поле объекта которое задает из какого EventLoop он будет получать сигналы с QueuedConnection
Цитировать
- В вашу деревню провели электричество, керосиновые лампы больше не нужны, просто вот тут включаете - и свет горит! Все поняли?

- Да, все ясно! А куда керосин заливать ??


Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: demaker от Май 26, 2016, 15:07
moveToThread изменяет thread() и тем самым объект перемещается в другой поток. Так???
Никто никуда не "перемешается", меняется поле объекта которое задает из какого EventLoop он будет получать сигналы с QueuedConnection
Цитировать
- В вашу деревню провели электричество, керосиновые лампы больше не нужны, просто вот тут включаете - и свет горит! Все поняли?

- Да, все ясно! А куда керосин заливать ??

 :D :D :D ;D ;D ;D ;D

Теперь ясно :) Спасибо большое за разъяснения!



Название: Re: moveToThread(), exec(),Qt::DirectConnection, Qt::QueueConnection
Отправлено: qate от Май 27, 2016, 12:15
Простите, что задаю идиотские вопросы  :), но еще раз :)

https://wiki.qt.io/QThreads_general_usage