Russian Qt Forum
Ноябрь 22, 2024, 21:23 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: sockets + threads, помогите :(  (Прочитано 35170 раз)
yesrus
Гость
« : Март 12, 2009, 16:48 »

Здравствуйте!
Вообщем возникла проблема - подскажите пожалуйста как переделать пример blockingfortuneserver так, чтобы поток был не блокируемым (т.е. работал exec() евент луп в дочернем потоке).

Идея такова - есть интерфейс (кнопочки..поля и т.д.), программа после загрузки настроек посылает сигнал коннект в тренд и оттуда (а не в основном потоке) создается коннект к серверу, далее в этом дочернем потоке связывается сигнал readyRead с слотом, при этом слот так же выполняется при вывове в дочернем потоке, после обработки данных слот должен посылать сигнал в основной поток (в котором крутится интерфейс) и на основе данных пришедших с сигналом в нем меняются данные (т.е. пишется в textedit лог ...меняется цвет кнопок или их состояний и т.д.).
И основная проблема-как из главного потока (там где крутится интерфейс) послать сигнал(с данными) дочернему потоку(там где крутится сокет и установлен коннект с сервером).

итого:

1) как после(!) загрузки опций(в основном потоке) или клика кнопки в основном потоке вызвать дочерний(при этом передать ему параметры\опции которые были считанны в основном потоке) в котором создать сокет и установить коннект к серверу
2) как посылать сигнал из дочернего потока в основной (при получении данных (т.е. при получении данных в дочернем потоке слот вызывает по readyRead слот, слот обрабатывает данные и посылает результат с помощью emit в основной поток где на основе этих данных что либо меняется (кнопки и т.д.)))
3) как из основного потока посылать сигнал  на вызов слота в дочернем потоке (т.е. передать данные в слот в дочернем потоке (а слот после обработки передаст данные в сокет).(!!!) - это основная проблема т.к. тот же сокет я могу запустить в потоке, более того могу получить данные из него(в интернете полно примеров) т.к. могу связать его сигнал со слотом в основном потоке, НО я не могу вызвать слот дочернего потока (и сделать там write в сокет) из основного...пишет ерор...невозможно использовать сокет т.к. он в другом потоке (так и есть т.к. слот выполняется из основного потока т.к. его оттуда вызвали) так вот как вызвать слот таким образом, чтобы он отработал в дочернем потоке, а не в основном ? - этого нигде нет, потратил 2 дня на поиски и результата 0.

Единственная просьба - пишите пожалуйста как и что нужно изменить в примере blockingfortuneclient (не просто ссылки на функции..а весь синтаксис т.к. я новичек и многих нюансов не знаю...)
Заранее огромное спасибо за помощь!

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

п.п.п.с. мануал читал..много раз...по слоты и сигналы так же..но т.к. там нет примеров связи между тредами (только общие фразы и схемы) то ничего не понятно...
Записан
EhTemka
Гость
« Ответ #1 : Март 12, 2009, 21:52 »

...
И основная проблема-как из главного потока (там где крутится интерфейс) послать сигнал(с данными) дочернему потоку(там где крутится сокет и установлен коннект с сервером).

А зачем сигнал?

Можно воспользовался мютексом.

Например myTread.h:
Код
C++ (Qt)
 
class MyThread : public QThread
{
 
public:
....
 
   void addUserData(UserData *userData);
 
protected:
   void run();
 
private:
 
QMutex mutex_;                          
QQueue<UserData *> listUserData_; // данные от основного потока    
 
};
 

myTread.cpp
Код
C++ (Qt)
 
....
 
void MyThread::addUserData(UserData *userData)
{
   QMutexLocker locker(&mutex_);  
   listUserData_.enqueue(userData);    
}
 
void MyThread::run()
{
   UserData *userData;
   for (;;) {
       mutex_.lock();
 
       userData= *listUserData_.begin();
       listUserData_.dequeue();
 
       mutex_.unlock();
 
      .....
 
      delete userData;
 
     ....
  }
}
 
 

и где-нибудь в основном потоке
Код
C++ (Qt)
 
 
...
 
thread->addUserData(new UserData(/*data*/));
 
...
 
 

Записан
yesrus
Гость
« Ответ #2 : Март 12, 2009, 22:47 »

Хотелось бы без циклов, именно в луп эвенте все обрабатывать т.к. тот же readyRead не будет работать в цикле (т.к. это сигнал и без эвент лупа он не работает). =(

п.с. как я понимаю цикл это еще и большая нагрузка на процессор ? =(
« Последнее редактирование: Март 12, 2009, 22:59 от yesrus » Записан
BRE
Гость
« Ответ #3 : Март 12, 2009, 23:07 »

Код
C++ (Qt)
class Thread : public QThread
{
public:
   Thread();
 
   void run();
 
public slots:
   void    sendData( const QString &str );
 
signals:
   void    dataReaded( const QString &str );
 
private:
   QTcpSocket    *m_socket;
};
 
Thread::Thread()
{
   ...
   m_socket = new QTcpSocket;
   ...
}
 
void Thread::run()
{
   m_socket->moveToThread( this );
   connect( m_socket, SIGNAL( readyRead() ), SLOT( readData() ) );
   ....
   exec();
}
 
void Thread::sendData(...)
{
   m_socket->write( .... );
}
 
void Thread::readData()
{
   m_socket->read( ... );
   emit dataReaded(....);
}
 
Создавай объект, связывай сигналы/слоты.
Или в чем проблема?
« Последнее редактирование: Март 12, 2009, 23:08 от BRE » Записан
yesrus
Гость
« Ответ #4 : Март 12, 2009, 23:42 »

Я так и сделал первый раз...на что qt ругался...почти матом  Веселый (единственное отличие от Вашего кода-я сокет создавал сразу в run()

Сейчас написал:
Код:
class tcpclient : public QThread
{
    Q_OBJECT

public:
    tcpclient();
   ~tcpclient();

public slots:
    void sendData( const QString &str );


signals:
    void recvData( const QString &str );

private:
    QTcpSocket    *m_socket;
    QString hostName;
    quint16 port;

protected:
    void run();

};

tcpclient::tcpclient()
{
qDebug() << "Thread::constructor: " << QThread::currentThreadId();
m_socket = new QTcpSocket;
start();
}
tcpclient::~tcpclient()
{
}

void tcpclient::run()
{
  qDebug() << "Thread::run: " << QThread::currentThreadId();

  m_socket->moveToThread( this );
  connect( m_socket, SIGNAL( readyRead() ), SLOT( readData() ) );
  hostName = "test.ru";
  port = 5000;
  m_socket->connectToHost(hostName, port);
  exec();
}

void tcpclient::readData()
{
    qDebug() << "WorkerReadData: " << QThread::currentThreadId();
}

void tcpclient::sendData(const QString &str)
{

    qDebug() << "WorkerSendData: " << QThread::currentThreadId();
    m_socket->write("hi");
}
Далее в мейн треде создал объект:

tcpclient worker;
connect(this, SIGNAL(sendData(const QString &str )),worker, SLOT(sendData(const QString &str )));


И получил такой ответ в дебаге:

Цитировать
Thread::constructor:  0x28c
Thread::run:  0xf68
QObject::moveToThread: Current thread (0x3e4ac8) is not the object's thread (0x22fd00).
Cannot move to target thread (0x22fd00)

QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x3ea188), parent's thread is QThread(0x3e4ac8), current thread is tcpclient(0x22fd00)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x3ea188), parent's thread is QThread(0x3e4ac8), current thread is tcpclient(0x22fd00)
ThreadMain:  0x28c
QThread: Destroyed while thread is still running
QObject: Cannot create children for a parent that is in a different thread.

Собственно именно про это я и говорил в 1-м посте =(
п.с. QThread::currentThreadId(); показывает, что и в каком треде отрабатывает.
« Последнее редактирование: Март 12, 2009, 23:45 от yesrus » Записан
igor_bogomolov
Гость
« Ответ #5 : Март 13, 2009, 00:09 »

Написал проектик. Исходнички прилогаются.
Компилируй.
Запускай Qt Demo -> Fortune Server, указывай выделенный порт.
Надеюсь поможет. 
« Последнее редактирование: Март 13, 2009, 03:39 от igor_bogomolov » Записан
BRE
Гость
« Ответ #6 : Март 13, 2009, 00:18 »

Я запарился, да.  Улыбающийся m_socket->moveToThread( this ) переносим в конструктор.
После этого у меня работает, по крайней мере к fortuneserver подключается и ответ от него читает.
Записан
igor_bogomolov
Гость
« Ответ #7 : Март 13, 2009, 00:25 »

2 BRE Может легче m_socket = new QTcpSocket; в run-не делать, тогда moveToThread ненужен.
Или я чего-то недопонимаю?

И еще вопрос по moveToThread. У меня есть непониманиее этой функции.
Почему ей this передается, а не указатель на QThread? Run выполняется в другом потоке, отличном от того  в котором выполняется конструктор класса. Как ей передать указатель на поток в Run?
« Последнее редактирование: Март 13, 2009, 00:44 от igor_bogomolov » Записан
yesrus
Гость
« Ответ #8 : Март 13, 2009, 01:13 »

2BRE - как я и говорил ругается при вызове слота sendData
Цитировать
Thread::constructor:  0xdb4
Thread::run:  0xb3c
GuiThread:  0xdb4
WorkerReadData:  0xdb4
Далее нажимаю кнопку Send и в дебаге вижу
Цитировать
WorkerSendData:  0xdb4
QSocketNotifier: socket notifiers cannot be enabled from another thread

2igor_bogomolov - Спасибо за пример, но в нем нет как раз главной части - отправка сообщений из основного потока в сокет.

п.с. и почему то программа во время запуска только на долю секунды показывает досовское окошко и мгновенно завершает работу, в дебаге
Цитировать
exited with code 0
« Последнее редактирование: Март 13, 2009, 01:17 от yesrus » Записан
igor_bogomolov
Гость
« Ответ #9 : Март 13, 2009, 01:37 »

Подразумевалось общение с fortune server, оно все равно сообщения не обрботает.
Обновил архив - теперь с отправкой сообщения, бесполезной в данном случае, но все же

« Последнее редактирование: Март 13, 2009, 03:42 от igor_bogomolov » Записан
yesrus
Гость
« Ответ #10 : Март 13, 2009, 02:00 »

Вставил дебаг, результат ниже
Цитировать
WorkerConstructor:  0xdd4
Main:  0xdd4
WorkerRun:  0xd90
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x50a42a8), parent's thread is ThreadSocket(0x50a2300), current thread is QThread(0x3e4bc0)
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QTcpSocket(0x50a42a8), parent's thread is ThreadSocket(0x50a2300), current thread is QThread(0x3e4bc0)
WorkerConnectToHost:  0xdd4
WorkerConnected:  0xdd4
WorkerReadData:  0xdd4
QObject::connect: Cannot queue arguments of type 'QAbstractSocket::SocketError'
(Make sure 'QAbstractSocket::SocketError' is registered using qRegisterMetaType().)
WorkerSendData:  0xdd4

Как видно из лога сокет создается в основном треде (как результат подвешивает интерфейс на 5 сек при проверке Network operation timed out). =(
Записан
igor_bogomolov
Гость
« Ответ #11 : Март 13, 2009, 02:11 »

Попробуй добавить в конструктор Client
Код
C++ (Qt)
threadSocket->moveToThread(threadSocket);

Что пишет после добавления?
Записан
igor_bogomolov
Гость
« Ответ #12 : Март 13, 2009, 02:28 »

У меня
Цитировать
warning: main TID: 0xe14
warning: run TID: 0xa90
Записан
yesrus
Гость
« Ответ #13 : Март 13, 2009, 02:29 »

Изначально все, что в run() - выполняется в другом треде, это видно по WorkerRun:  0xd90, но как только идет попытка коннекта к серверу выскакивает QObject: Cannot create children for a parent that is in a different thread. потому, что сигнал на вызов слота приходит из мейнтреда, и как результат идет попытка выполнения слота в мейн треде -> ошибка т.к. сокет создан в другом треде(странно то, что в данном случае оно работает в основном...т.е сокет переносится в основной поток автоматически после ошибок...).
« Последнее редактирование: Март 13, 2009, 02:34 от yesrus » Записан
igor_bogomolov
Гость
« Ответ #14 : Март 13, 2009, 02:36 »

а moveToThread добавили?
У меня после добавления все правильно заработало.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.19 секунд. Запросов: 21.