Russian Qt Forum

Qt => Многопоточное программирование, процессы => Тема начата: RedDog от Октябрь 21, 2011, 14:15



Название: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 21, 2011, 14:15
Есть основной поток, в котором создаются дочерние, при создании нового дочернего потока, необходимо передать в уже работающие некоторое значение, через сигнал/слот почему то не работает:
Код:
class CMain
{
...
    void GenerateThreads();
signals:
    void setValue(int);
};

void CMain::GenerateThreads()
{
    for (int i = 0; i< 10; i++)
    {
        WorkThread *thread = new WorkThread();
        connect(this, SIGNAL(setValue(int)), thread, SLOT(on_setValue(int)));
        thread->start();
        emit setValue(i);
    }
}

class WorkThread: public QThread
{
...
    void run();
signals:
    void setValue(int)
slots:    
    void on_setValue(int)
};

void WorkThread::run()
{
    CWorker *worker = new CWorker();
    connect(this, SIGNAL(setValue(int)), worker, SLOT(on_setValue(int)));
    worker->doWork();
    exec();
}

void WorkThread::on_setValue(int _value)
{
    emit setValue(_value);    
}

class CWorker
{
...
    int a;
    void doWork();  
slots:    
    void on_setValue(int)
};

void CWorker::on_setValue(int _value)
{
    a = _value;
}

void CWorker::doWork()
{
    while (tue)
    {
        int b = a;
        msleep(b);
    }    
}

Как правильно оповестить дочерние потоки об изменении какого либо параметра? При том что они в цикле что то считают с перерывами на сон.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Странник от Октябрь 21, 2011, 15:17
дочерние потоки крутятся у вас в doWork(), а цикл обработки событий тем временем не запущен. по-моему, лучше будет создать в потоке таймер, по сигналу которого будет вызываться слот doWork().


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 21, 2011, 21:01
doWork() это просто отдельная функция объекта, который работает в дочернем потоке.
Вообще тут смысл не в таймере, а в том, что есть условно бесконечный цикл в объекте дочернего потока, и необходимо управлять определенными данными внутри этого бесконечного цикла, причем управление должно осуществляться из главного потока.
Т.е. вопрос можно переформулировать так: как "заморозить" выполнение дочернего потока, изменить в нем данные, и запустить его на продолжение с новыми данными, при условии, что в дочернем потоке есть бесконечный цикл?


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Igors от Октябрь 21, 2011, 21:27
дочерние потоки крутятся у вас в doWork(), а цикл обработки событий тем временем не запущен.
Логичное соображение, но дело довольно темное, каким-то образом Qt умеет хранить события до тех пор пока не запустится eventLoop.

Т.е. вопрос можно переформулировать так: как "заморозить" выполнение дочернего потока, изменить в нем данные, и запустить его на продолжение с новыми данными, при условии, что в дочернем потоке есть бесконечный цикл?
Непонятно какой цикл имеется ввиду - свой или exec() (который Вы тоже запускаете). Если свой, то все то что уже многократно обсуждалось для "как остановить": нитка проверяет флажок, если он взведен, то забирает новые данные и работает с ними. Понятно что проверку надо вставлять в код нитки (и возможно во многие места), а также что "сразу" нитка перестроиться не сможет. Но никаких волшебных/легких путей здесь нет.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Странник от Октябрь 21, 2011, 23:28
doWork() это просто отдельная функция объекта, который работает в дочернем потоке.
Вообще тут смысл не в таймере, а в том, что есть условно бесконечный цикл в объекте дочернего потока, и необходимо управлять определенными данными внутри этого бесконечного цикла, причем управление должно осуществляться из главного потока.
Т.е. вопрос можно переформулировать так: как "заморозить" выполнение дочернего потока, изменить в нем данные, и запустить его на продолжение с новыми данными, при условии, что в дочернем потоке есть бесконечный цикл?
собственно я вам и предлагаю в качестве условно-бесконечного цикла использовать цикл обработки событий потока. для этого функцию doWork необходимо сделать слотом. это позволит выполнять doWork в потоке, не прерывая его работу при возврате из этого слота, а также запускать его в потоке уже после запуска цикла обработки событий. таким образом ваш поток сможет обрабатывать накопившиеся в очереди события (например, вызов слота on_setValue) после завершения doWork(), либо в процессе ее выполнения с помощью вызова processEvents().
Код:
class CMain
{
...
    void GenerateThreads();
signals:
    void setValue(int);
};

void CMain::GenerateThreads()
{
    for (int i = 0; i< 10; i++)
    {
        WorkThread *thread = new WorkThread();
        connect(this, SIGNAL(setValue(int)), thread, SLOT(on_setValue(int)));
        thread->start();
        QMetaObject::invokeMethod(thread, SLOT(doWork()), Qt::QueuedConnection);
        emit setValue(i);
    }
}

class WorkThread: public QThread
{
...
    int a;
signals:
    void setValue(int);
slots:    
    void doWork();
    void on_setValue(int);
};

void WorkThread::on_setValue(int _value)
{
    a = _value;
}

//вариант 1
void WorkThread::doWork()
{
        int b = a;
        ...
        QTimer::singleShot(b, this, SLOT(doWork()));
}    

//вариант 2
void WorkThread::doWork()
{
        while (true)
        {
            int b = a;
            ...
            msleep(b);
            qApp()->processEvents();
        }
}    

Логичное соображение, но дело довольно темное, каким-то образом Qt умеет хранить события до тех пор пока не запустится eventLoop.
хранить-то хранит, но не обрабатывает. разве что попробовать processEvents вызвать.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 09:28
Непонятно какой цикл имеется ввиду - свой или exec() (который Вы тоже запускаете). Если свой, то все то что уже многократно обсуждалось для "как остановить": нитка проверяет флажок, если он взведен, то забирает новые данные и работает с ними. Понятно что проверку надо вставлять в код нитки (и возможно во многие места), а также что "сразу" нитка перестроиться не сможет. Но никаких волшебных/легких путей здесь нет.
[/quote]Цикл имеется ввиду свой while(true) который запускается внутри exec().
Что бы остановить его надо ввести какое то значение, а оно не вводится, т.к.  цикл бесконечный и не отвечает на внешние запросы, т.е. до его окончания объект не может реагировать на сигналы извне.
prcessEvent внутри цикла был бы выходом, но он только для QCoreApplication, а как его реализовать для отдельного самописного класса хз.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: BRE от Октябрь 22, 2011, 09:33
А если цикла while(true) заменить на:
Код
C++ (Qt)
bool running = true;
...
while( running )
{
}
 
или
Код
C++ (Qt)
bool stopped = false;
...
while( true )
{
   if( stopped )
       break;
}
 

;)

P.S. Кстати и цикла обработки событий внутри своих циклов легко крутить, если воспользоваться классом QEventLoop.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Странник от Октябрь 22, 2011, 10:56
prcessEvent внутри цикла был бы выходом, но он только для QCoreApplication, а как его реализовать для отдельного самописного класса хз.
вызов QCoreApplication::processEvents() производит обработку очереди событий для вызывающего потока, что вас не устраивает?


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 11:31
А если цикла while(true) заменить на:
Код
C++ (Qt)
bool stopped = false;
...
while( true )
{
   if( stopped )
       break;
}
 
Вот грубо говоря мне эту bool stopped и надо изменять, но оно не изменяется, т.к. не доходит сигнал, что надо ее изменить, т.к. крутится бесконечный цикл и обработка сообщений не происходит.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 11:33
prcessEvent внутри цикла был бы выходом, но он только для QCoreApplication, а как его реализовать для отдельного самописного класса хз.
вызов QCoreApplication::processEvents() производит обработку очереди событий для вызывающего потока, что вас не устраивает?
т.е. если вызвать:

Код:
while(true)
{
    QCoreApplication::processEvent();
   /// bla bla bla
}
с учетом того, что этот цикл в дочернем потоке, то поможет? (имеется ввиду поможет конкретному дочернему потоку)


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Igors от Октябрь 22, 2011, 12:02
Вот грубо говоря мне эту bool stopped и надо изменять, но оно не изменяется, т.к. не доходит сигнал, что надо ее изменить, т.к. крутится бесконечный цикл и обработка сообщений не происходит.
Главная нитка изменяет stopped, а дочерняя это может отловить - в любой цикл можно вставить if (stopped)


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 15:32
Вот грубо говоря мне эту bool stopped и надо изменять, но оно не изменяется, т.к. не доходит сигнал, что надо ее изменить, т.к. крутится бесконечный цикл и обработка сообщений не происходит.
Главная нитка изменяет stopped, а дочерняя это может отловить - в любой цикл можно вставить if (stopped)
Если посмотреть код из первого поста, то я так и делаю, но через сигнал/слот это не проходит,т.е. цикл обработки сообщений зависает до окончания работы while(true).
Как заставить отработать цикл обработки  сообщений?


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Igors от Октябрь 22, 2011, 16:01
Если посмотреть код из первого поста, то я так и делаю, но через сигнал/слот это не проходит,т.е. цикл обработки сообщений зависает до окончания работы while(true).
Как заставить отработать цикл обработки  сообщений?
То чего нет зависнуть не может, eventLoop создается в exec(). Если я правильно понял Вы хотите все делать на сигналах, без самопальных флажков. Тогда в дочерней нитке просто войдите в exec (не перекрывайте run). Нитка будет ждать сигнала/события. Пошлите его из главной, нитка начнет свое doWork, в котором уже можно звать processEvents и принимать следующие сигналы.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 18:42
Исходя из Ваших слов у меня вырисовывается картина кода, точно такая же как я привел в 1-м посте.
Но она, увы, не работает
Если не сложно, покажите псевдокодом.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Странник от Октябрь 22, 2011, 19:25
Исходя из Ваших слов у меня вырисовывается картина кода, точно такая же как я привел в 1-м посте.
Но она, увы, не работает
Если не сложно, покажите псевдокодом.
такой вариант я приводил несколькими постами выше:
Код:
class CMain
{
...
    void GenerateThreads();
signals:
    void setValue(int);
};

void CMain::GenerateThreads()
{
    for (int i = 0; i< 10; i++)
    {
        WorkThread *thread = new WorkThread();
        connect(this, SIGNAL(setValue(int)), thread, SLOT(on_setValue(int)));
        thread->start();
        QMetaObject::invokeMethod(thread, SLOT(doWork()), Qt::QueuedConnection);
        emit setValue(i);
    }
}

class WorkThread: public QThread
{
...
    int a;
signals:
    void setValue(int);
slots:   
    void doWork();
    void on_setValue(int);
};

void WorkThread::on_setValue(int _value)
{
    a = _value;
}

//вариант 1
void WorkThread::doWork()
{
        int b = a;
        ...
        QTimer::singleShot(b, this, SLOT(doWork()));
}   

//вариант 2
void WorkThread::doWork()
{
        while (true)
        {
            int b = a;
            ...
            msleep(b);
            qApp()->processEvents();
        }
}   


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 22, 2011, 21:00
такой вариант я приводил несколькими постами выше:
Попробую 2-й вариант.1-й в моем случае не подойдет, ибо слишком много уже готового и отлаженного кода надо будет переделывать.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: Igors от Октябрь 23, 2011, 09:02
Код
C++ (Qt)
WorkThread *thread = new WorkThread();
connect(this, SIGNAL(doWork(int)), thread, SLOT(on_doWork(int)), Qt::QueuedConnection));
connect(this, SIGNAL(setValue(int)), thread, SLOT(on_setValue(int)), Qt::QueuedConnection));
thread->start();
emit doWork(i);
emit setValue(i);
 
Принципиально это ничем не отличается от того что привел Странник. Имеется ввиду что doWork будет делаться в слоте и что run просто запустит exec (как по умолчанию). Т.к. цикл событий запущен, дочерняя нитка сможет ловить сигналы в doWork

Код
C++ (Qt)
void doWork(..)
{
while (true) {
 ...
 QApplication::processEvents();  // если в очереди setValue, то его слот сработает
 if (mValueChanged) {               // данные изменились
 ...
}
}
 


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 23, 2011, 21:22
А что, если вызывать QCoreApplication::processEvent() непосредственно после испускания сигнала, а не в цикле каждый раз? Т.е.:
Код
C++ (Qt)
emit valueChange(i);
QCoreApplication::processEvent();
отработает,или все же есть какая то очередь (последовательность) действий, что может  не успеть?


Название: Re: Передать значение из главного потока в дочерний
Отправлено: BRE от Октябрь 23, 2011, 21:28
Не поможет. Событие о сигнале кладется в очередь сообщений нити приемника и что бы это событие сработало - нужно крутануть цикл обработки событий именно этой нити.


Название: Re: Передать значение из главного потока в дочерний
Отправлено: RedDog от Октябрь 24, 2011, 21:09
Попробовал вариант с processEvents(). Работает. Тему вроде как можно пометить решенной.
Хотя имхо это некоторый индусизм, т.е. не очень красиво. Буду думать над 1-м вариантом.