Поиск:

Ответ в темуСоздание новой темы Создание опроса
> moveToThread ( QThread * targetThread), Нужно ли мне использовать ее в задаче 
V
    Опции темы
null56
Дата 22.12.2008, 13:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



Не могу до конца понять назначение это фукнции, особенно ее подтекст NOT THREAD SAFE.
У меня задача состоит в следующем
1) Есть порожденная нить (несколько на самом деле), работает в своем eventloop
2) Один поток, основной управляющий сервер, который создает сокеты и перемещает их в матрицу нитей

То есть на псевдокоде это будет выглядеть вот так:
Код

1) Есть конект QSslSicket 
2) Добавить его в матрицу QSslSocket ** mMatrix
3) Соединить нужные сигналы cсокета со слотами вторичной нити


Вопросы
1) Нужно ли делать moveToThread для QSslSocket?
2) Что мне даст функция moveToThread?
3) В описании, что подразумевается под поточно НЕ безопасная?
PM MAIL   Вверх
SABROG
Дата 22.12.2008, 16:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 81
Всего: 91



Тут такая система. Если ты создаешь QThread в главном event loop'e, то QThread становится ребенком главного потока (он все-таки QObject), и все слоты и методы выполняются в главном потоке (конструктор в том же числе), кроме метода run(). Все объекты созданные в run() уже дети отдельного - нового потока. 

Предположим, что ты захотел наладить систему сообщений между GUI и потоком. Аля нажал на кнопочку и в потоке запустился долгоиграющий процесс. И у тебя ничего не получится, т.к. и GUI и нить - детки главного потока. Это приведет к зависанию GUI до того момента, пока слот нити не вернет управление. В тоже время, если коннектить сигналы к объектам созданных в run()'е, то слоты этих объектов уже будут выполняться в новом потоке. Так вот moveToThread был создан для того, чтобы перенести объект в другой поток, чтобы для объектов, детишек этого потока существовал личный eventLoop со своими сигналами и эвентами. Кроме того у переносимого объекта не должно быть родителей. Насчет thread safe я сам до конца понять не могу, может ты поймешь, посмотри в ассистенте "Reentrancy and Thread-Safety".


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
null56
Дата 22.12.2008, 17:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



Отлично, спасибо.. так намного понятнее
У меня ситуация такая, что QSslSocket создается в основном потоке, а я хочу, чтобы слоты обрабатывались в дочернем порожденым, получается мне надо будет сделать sslsocket->moveToThread и в качестве аргумента передать объект класса QThread или как... ?

Просто вот тут и пишут про небезопасность межнетевых связях
Цитата

Warning: This function is not thread-safe; the current thread must be same as the current thread affinity. In other words, this function can only "push" an object from the current thread to another thread, it cannot "pull" an object from any arbitrary thread to the current thread.


попробую перевезти суть... Функция ните небезопасна. Текущая нить должна быть такой же как и нитевая строение (связь, на основании связей... хз)  иными словами функция может только класть объект из текущей нити для того, чтобы положить в другую, но не может вытащить объект из любой другой нити, чтобы вставить в текущую

Код должен быть примерно таким?

Код

QThread my_thread;
MyThread.start();

QSslSocket my_socket;

connect (&my_socket, SIGNAL(...), &MyThread, SLOT(...));
...

my_socket.movetoThread(&my_thread);  // так получается, или я передам не дочерние нити?


И второй момент, если объект у меня был ранее связан с некоторым родителей, то я должен сам разорвать эту связь? setParent(0);...?

Добавлено через 5 минут и 2 секунды
нет, наверное правильнее будет??/
Код

my_socket.movetoThread(my_thread.thread());


Добавлено через 7 минут и 59 секунд
не... вот так наверное
Код

my_socket.movetoThread(my_thread.currentThread () );


это же функция, которая вернет дочеринй поток класса QThread
PM MAIL   Вверх
null56
Дата 22.12.2008, 17:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



не... так вообще можно получить адрес потока, который был запущен в run или нет?
PM MAIL   Вверх
SABROG
Дата 22.12.2008, 17:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 81
Всего: 91



Я подобные вещи не проделывал. Попробуй и &my_thread передавай и setParent(0) ставь.
В принципе moveToThread можно вызвать в конструкторе новой QThread.

Это сообщение отредактировал(а) SABROG - 22.12.2008, 18:30


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
null56
Дата 22.12.2008, 19:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



Я попробую.
В конструкторе уже не получится, у меня потоки создаются при запуске программы и их определенное количество, они обслуживают матрицу сокетов....
Надо подумать, а главное поиграться, чтобы знать точно. У потока же есть идентификатор? или что - то там похожее, чтобы в обработке события точно знаТЬ. что за поток.
Ну если не получится, то тогда другим путем можно сделать, чтобы проходя по матрице сокетов, поток узнавал готовые к выполнению ячейки и сам добавлял их к себе...

Завтра на работе посиотрю и отпишусь
PM MAIL   Вверх
null56
Дата 23.12.2008, 19:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



В общем ситуация такая, это проверено фактами и хочу показать на примере, отсюда и ответ на предудущий мой пост, как заставить выполняться слоты во вторичных потоках...
В качестве примера я приведу 2 класса и файлик main.cpp, для определения выполняющего потока я использовал статическую функцию
Код

QThread * QThread::currentThread () 

для вывода в консоль адреса потока, который у меня не изменяется и так опишу первый посредственный класс, он не меняется и имеет всего один метод, который генерит свой же сигнал my_signal()

Код

class test_threads : public QObject
{
Q_OBJECT

public:
    test_threads();
    void emitSig(int i) {emit my_signal(i);} // ее надо перенести в спп

signals:
    void my_signal(int);
};


Класс для потока не изменяется, лишь его функция run()
Код

class ServiceThread : QObject
{
    Q_OBJECT
        void setTest(test_threads * t) {test = t;}
        test_threads * test;

        void ServiceThread :: emitSub(int i)
        {
             test->emitSig(i);
         }

    public slots:
        void ServiceThread :: showMessage(int i)
              {  
                   qDebug("New slot");
                   qDebug("Thread address: %d",  QThread::currentThread ());
                  qDebug("My message = %d", i);
              }

    protected:
        void run();
};


Функция run проста
Код

    qDebug("INSIDE RUN Thread address: %d",  QThread::currentThread ());
    exec();


Так вот изменяя различные варианты: создание переменной типа test_thread, хоть в основном евенте, хоть в дочерних, соединяя сигналы где угодно....
ЕСЛИ НЕ СДЕЛАЕШЬ: ServiceThread my_thread.moveToThread(&my_thread); выполянться все будет в основном евенте. То есть эта функция ключевая

Вот пример мейна, где все делается в основной функции
Код


 int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    qDebug("Main EVENT LOOP: %d",  QThread::currentThread ());

    ServiceThread * thread = new ServiceThread;
    thread->moveToThread(thread);

    test_threads * p_tt = new test_threads;
    QObject :: connect(p_tt, SIGNAL(my_signal(int)), thread, SLOT(showMessage(int)));

    //-
    qDebug("FIRST EMIT");             
    p_tt->emitSig(1);
    thread->setTest(p_tt);

    //-
    qDebug("Start");
    thread->start();

    //-
    qDebug("SECOND EMIT");
    thread->emitSub(2);
    //-

    sleep(3);
    qDebug("wake");
    thread->stopEventLoop();
}


Вывод в консоль получился следующим
Код

Main EVENT LOOP: 146831264
FIRST EMIT
Start
INSIDE RUN Thread address: 147000192
SECOND EMIT
New slot
Thread address: 147000192
My message = 1
New slot
Thread address: 147000192
My message = 2
wake
stop thread


Хочу заметить, что первый раз сигнал генериться до запуска функции start
PM MAIL   Вверх
SABROG
Дата 23.12.2008, 20:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 81
Всего: 91



Цитата(null56 @  23.12.2008,  19:18 Найти цитируемый пост)
Так вот изменяя различные варианты: создание переменной типа test_thread, хоть в основном евенте, хоть в дочерних, соединяя сигналы где угодно....
ЕСЛИ НЕ СДЕЛАЕШЬ: ServiceThread my_thread.moveToThread(&my_thread); выполянться все будет в основном евенте. То есть эта функция ключевая


А создай test_thread внутри метода ServiceThread::run() и попробуй без moveToThread.

Это сообщение отредактировал(а) SABROG - 23.12.2008, 20:16


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
null56
Дата 23.12.2008, 21:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



Пожалуйста

Функция run()
Код

void ServiceThread ::  run()
{
    qDebug("RUN address: %d",  QThread::currentThread ());
    test = new test_threads;
    connect(test, SIGNAL(my_signal(int)), this, SLOT(showMessage(int)));
    test->emitSig(3);
//    exec();   
    qDebug("exit");
}


Функция main
Код

QApplication a(argc, argv);

   qDebug("Main EVENT LOOP: %d",  QThread::currentThread ());
    ServiceThread * thread = new ServiceThread;
    //-

    qDebug("Start");
    thread->start();
    //-

    qDebug("FIRST EMIT");
    thread->emitSub(1);
    //-

   qDebug("SECOND EMIT");
    thread->emitSub(2);
    //-

    sleep(5);
    qDebug("wake");
    thread->stopEventLoop();

    return a.exec();


Вывод в консоль
Код

Main EVENT LOOP: 5293072
Start
RUN address: 5340624
exit
FIRST EMIT
SECOND EMIT
wake
stop thread
New slot
Thread address: 5293072
My message = 3
New slot
Thread address: 5293072
My message = 1
New slot
Thread address: 5293072
My message = 2


Все как вы и писали, конструктор и слоты объекта thread объявлены в главном цикле, а значит там и будут выполняться.

В основной функции я вызываю функция start, в запущенной функции run создается объект test и соединяется с сигналами, затем основная ветвь спит и ее евентлуп не может обработать слоты, в то время как в ран сам запускает в очередь еще один сигнал номер 3, но он оказывается раньше, чем 1 и 2. Все ждут, пока первичная нить проснется, когда просыпается, выполняет слоты в порядке поступления сигналов...

Если мы хотим, чтобы сигналы их основного потока были первыми, то добавим слово 
Код

sleep(5);

в функцию run после строчки, где идет коннект сигналов со слотами... и тогда сигналы из первичного потока обгонят сигнал из вторичного
Что и видно из 
Код

Main EVENT LOOP: 5293072
Start
RUN address: 5340624
FIRST EMIT
SECOND EMIT
wake
stop thread
New slot
Thread address: 5293072
My message = 1
New slot
Thread address: 5293072
My message = 2
New slot
Thread address: 5293072
My message = 3
exit


Если я нигде не ошибся, то такое разделение и есть в qt.... как ты и писал выше
PM MAIL   Вверх
SABROG
Дата 23.12.2008, 21:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 81
Всего: 91



Если честно, у меня в голове сейчас полная каша. Так вариант без moveToThread возможен в твоей программе или нет ? Судя по тому, что я вижу eventloop потока отрабатывает как надо, отдельно от главного потока, что и требовалось сделать.


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
null56
Дата 23.12.2008, 21:58 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



Для моей программы именно он и нужен....

Цитата

Если честно, у меня в голове сейчас полная каша. Так вариант без moveToThread возможен в твоей программе или нет ? Судя по тому, что я вижу eventloop потока отрабатывает как надо, отдельно от главного потока, что и требовалось сделать.


Ну как же правильно?

Первичный поток имеет адрес
Код

Main EVENT LOOP: 5293072


он же и обрабатывает то, что создается в run()
Код

RUN address: 5340624


все слоты вызываются первичным потоком
Код

5293072


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

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

Добавлено через 2 минуты и 51 секунду
спасибо большое, что помог разобраться
PM MAIL   Вверх
SABROG
Дата 23.12.2008, 22:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Hacker
****


Профиль
Группа: Завсегдатай
Сообщений: 2481
Регистрация: 18.9.2006

Репутация: 81
Всего: 91



Что-то тут не так. Закомментированный 

Код

//    exec();


в run() должен запускать QEventLoop потока, который и должен, по-хорошему, вызывать слоты. Правда тут уже другой вопрос, обычно выпущенные сигналы моментально вызывают слот без добавления события в event. Может быть Qt::QueuedConnection и решил эту проблему, но все-таки.

Хотелось бы до конца разобраться, поэтому я нашел пример от одного гуру smile ,который утверждает, что его пример работает (что собственно подтверждает и топикстартер): http://www.forum.crossplatform.ru/index.ph...ost&p=10613

В догонку о печальном: http://www.prog.org.ru/index.php?topic=7889
Немного о неблокируемых сокетах на базе QSocketDevice: http://doc.trolltech.com/qq/qq09-networkthread.html

Это сообщение отредактировал(а) SABROG - 23.12.2008, 22:58


--------------------
Национальная группа Russian Federation на QtCentre.
PM MAIL   Вверх
null56
Дата 24.12.2008, 00:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 721
Регистрация: 19.3.2008

Репутация: 6
Всего: 12



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

И опять все потому что ты оказался прав.... еще раз повторю твои слова.... ну не буквально конечно
"Если ты создаешь объект QThread в функции main, значит у тебя все слоты принадлежат основному eventloop", если в в функции run(), то уже дочернему....

Именно так он и сделал, но я - то нет...

Давай посмотрим на его код, заметь, весь объект у него создается в run, включая ЕГО СЛОТЫ ЖЕ

Код

class CxPrint: QObject
{
    Q_OBJECT

public:
    void drawText();

signals:
    void signalDrawText();

protected slots:
    void slotDraw();

public:
    CxPrint(void);
    virtual ~CxPrint(void);
};


тут же в конструкторе
Код

CxPrint::CxPrint(void)
{
    connect( this, SIGNAL( signalDrawText()), this, SLOT( slotDraw()));
}


и все это в run
Код


void CxThread::run()
{
    {
    QMutexLocker locker(&m_mutex);
    static int n = 1;
    m_pPrint = new CxPrint;
       .................


Весь объект принадлежит вторичному или н-ному процессу... конечно здесь не нужен никакой moveTo
У него взаимодействие происходит через вызов метода класса, который уже вызовет у объекта CxThread метод, который в свою очередь уже будет вызывать метод созданного в run объекта


+ чтобы вызвать метод в дочернем потоке, надо делать emit перед вызовом метода объета, я об этом не знал

Единственное, что вот здесь, как показали опыты, делать не надо emit
Код

void CxThread::drawText()
{
    if( m_pPrint)
        emit m_pPrint->drawText();
}

и вот здесь
Код

      for( int n=0; n < sizeof( thread)/sizeof( *thread); ++n)
        {
            if( thread[n].isRunning())
                emit thread[n].drawText();
        }


Нужно ли здесь делать???? по хорошему?

Отлично, что разобрались... с этим вопросом
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Кроссплатформенное программирование, QT/Gtk+/wxWidgets"
JackYF
Любитель
  • В заголовке темы в квадратных скобках обозначьте используемую вами библиотеку, например: [QT],[GTK],[wx].
  • Если вопрос актуален только для некоторой версии библиотеки, либо, если вы пользуетесь не самой последней версией, укажите это. Например: [QT4], [GTK2].
  • Все начинающие изучать Qt - не забудьте зайти сюда.
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • В вопросе укажите полную версию версию библиотеки, а также все дополнительные используемые программные пакеты.
  • Не забывайте пользоваться кнопкой "Код".
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к тематике этого раздела. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, Любитель.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets | Следующая тема »


 




[ Время генерации скрипта: 0.0570 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.