Russian Qt Forum

Qt => Общие вопросы => Тема начата: kuzulis от Июнь 04, 2009, 20:43



Название: Опять потоки!
Отправлено: kuzulis от Июнь 04, 2009, 20:43
Доброго времени суток...

Столкнулся с проблемой:
мне нужно чтобы метод класса execMethod(), вызванный из основного потока приложения выподнился в другом потоке :)
код tc.h
Код:
#ifndef TC_H
#define TC_H

#include <QThread>
#include <QDebug>

class ThreadClass : public QThread
{
public:
ThreadClass(QObject *parent = 0) : QThread(parent) { }
void execMethod() {
qDebug() << "ThreadClass->execMethod()-> ID = " << QThread::currentThreadId();
}
protected:
void run() {
qDebug() << "ThreadClass->run()-> ID = " << QThread::currentThreadId();
exec();
qDebug() << "After Exec";
}
};

#endif

код main.cpp
Код:
#include <QtCore>
#include <tc.h>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
qDebug() << "QCoreApplication -> ID = " << QThread::currentThreadId();
ThreadClass *tc = new ThreadClass();
tc->moveToThread(tc);
tc->start();
sleep(1);
tc->execMethod();
       return app.exec();
}

вот что выводит:
Цитировать
[den@myhost thread_run]$ release/test
QCoreApplication -> ID =  140163681347328
ThreadClass->run()-> ID =  1115613520
ThreadClass->execMethod()-> ID =  140163681347328

а мне нужно чтобы получилось ThreadClass->execMethod()-> ID =  1115613520   !!!

Это реально сделать? Или я требую невозможного?



Название: Re: Опять потоки!
Отправлено: BRE от Июнь 04, 2009, 20:53
В контексте другого потока будет выполняться все, что находиться в методе run() и после завершения этого метода нить умрет. Если execMethod вызвать из run, то он будет выполнен в контексте нити.


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 04, 2009, 21:40
блина, только через сигналы работает:

tc.h
Код:
#ifndef TC_H
#define TC_H

#include <QThread>
#include <QDebug>

class PrintClass : public QObject
{
Q_OBJECT
public:
PrintClass() {}
virtual ~PrintClass() {}
public slots:
void printMsg() { qDebug() << "PrintClass->printMsg()-> ID = " << QThread::currentThreadId(); }
};

class ThreadClass : public QThread
{
Q_OBJECT
public:
ThreadClass() {}
virtual ~ThreadClass() {}
void execMethod() {
qDebug() << "ThreadClass->execMethod()-> ID = " << QThread::currentThreadId();
emit sigExec();
}
protected:
void run() {
qDebug() << "ThreadClass->run()-> ID = " << QThread::currentThreadId();
pc = new PrintClass();
connect(this, SIGNAL(sigExec()), pc, SLOT(printMsg()));
exec();
qDebug() << "After Exec";
}
signals:
void sigExec();
private:
PrintClass *pc;
};
#endif

main.cpp
Код:
#include <QtCore>
#include "tc.h"
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
qDebug() << "QCoreApplication -> ID = " << QThread::currentThreadId();
ThreadClass *tc = new ThreadClass();
tc->start();
sleep(1);
tc->execMethod();
    return app.exec();
}

выдало:
Цитировать
[den@myhost thread_run]$ release/test
QCoreApplication -> ID =  140552331060992
ThreadClass->run()-> ID =  1110485328
ThreadClass->execMethod()-> ID =  140552331060992
PrintClass->printMsg()-> ID =  1110485328

уфффффф


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 04, 2009, 23:05
а что, tc->execMethod() когда-нибудь где-нибудь выполнился бы в другом потоке?)
налицо пробел в понимании многопотокового программирования...


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 07:21
Цитировать
а что, tc->execMethod() когда-нибудь где-нибудь выполнился бы в другом потоке?)
налицо пробел в понимании многопотокового программирования...

Не поверите! Я по этому поводу ни сколько не переживаю ! :)



Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 08:08
и даже так работает:

tc.h
Код:
#ifndef TC_H
#define TC_H
#include <QThread>
#include <QDebug>
class ThreadClass : public QThread
{
Q_OBJECT
public:
ThreadClass() {}
virtual ~ThreadClass() {}
void execMethod() {
qDebug() << "ThreadClass->execMethod()-> ID = " << QThread::currentThreadId();
emit sigExec();
}
protected:
void run() {
qDebug() << "ThreadClass->run()-> ID = " << QThread::currentThreadId();
connect(this, SIGNAL(sigExec()), this, SLOT(printMsg()));
exec();
qDebug() << "After Exec";
}
signals:
void sigExec();
private slots:
void printMsg() { qDebug() << "ThreadClass->printMsg()-> ID = " << QThread::currentThreadId(); }
};
#endif

main.cpp
Код:
#include <QtCore>
#include <QtTest/QtTest>
#include "tc.h"
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
qDebug() << "QCoreApplication -> ID = " << QThread::currentThreadId();
ThreadClass *tc = new ThreadClass();
tc->moveToThread(tc);  // <---- ВОТ БЕЗ ЭТОГО НЕ ПРАВИЛЬНО РАБОТАЕТ
tc->start();
QTest::qSleep(250);
tc->execMethod();
    return app.exec();
}

вывод:
Цитировать
D:\projects\thread_run2>release\test.ex
QCoreApplication -> ID =  0x7d0
ThreadClass->run()-> ID =  0x358
ThreadClass->execMethod()-> ID =  0x7d0
ThreadClass->printMsg()-> ID =  0x358

удивидельно! :)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 05, 2009, 08:26
удивидельно! :)
А почему удивительно?  ;)
printMsg вызывается из run, соответственно в контексте нити.


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 05, 2009, 10:58
собсна, вообще не удивительно.
а вот tc->moveToThread(tc) улыбнуло...ассоциируется с картинкой, где чел сам себе в зад залазит /* может видел кто? */ :)


Название: Re: Опять потоки!
Отправлено: pastor от Июнь 05, 2009, 12:27
А я так и непонял, с какой целью вызывается tc->execMethod() в main о_О


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 05, 2009, 12:39
ну, это как синглШот...только без синглШот-а :)


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 14:11
Цитировать
собсна, вообще не удивительно.
а вот tc->moveToThread(tc) улыбнуло...ассоциируется с картинкой, где чел сам себе в зад залазит /* может видел кто? */ Улыбающийся

ну а как иначе то? И кстати я тут на форуме видел что именно так люди делают :)

Цитировать
А я так и непонял, с какой целью вызывается tc->execMethod() в main о_О

Ну дык можно и не в main... это просто тестовый вариант программки... Или вы о чем?

А вообще планы у меня глобального характера!! Как бы все сделать бы (осилить)  :)


Название: Re: Опять потоки!
Отправлено: pastor от Июнь 05, 2009, 14:22
Ну дык можно и не в main... это просто тестовый вариант программки... Или вы о чем?

Процетирую Константина:

Цитировать
а что, tc->execMethod() когда-нибудь где-нибудь выполнился бы в другом потоке?)


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 14:25
Цитировать
Ну дык можно и не в main... это просто тестовый вариант программки... Или вы о чем?

Процетирую Константина:

Цитировать
а что, tc->execMethod() когда-нибудь где-нибудь выполнился бы в другом потоке?)

ДА!!! если в последнем варианте убрать tc->moveToThread(tc) - то выполнится в главном потоке QApplication ... если добавить - то в дочернем потоке tc


Название: Re: Опять потоки!
Отправлено: pastor от Июнь 05, 2009, 15:27
ну а как иначе то?

Как иначе? Вызвать execMethod в run

И кстати я тут на форуме видел что именно так люди делают :)

За сколько лет на форуме я невидел такой вот реализации:

tc->moveToThread(tc)


Название: Re: Опять потоки!
Отправлено: SASA от Июнь 05, 2009, 15:41
Код:
void execMethod() {}
QFuture<void> QtConcurrent::run (execMethod)
И вот тебе функция в отдельном потоке...


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 16:02
Цитировать
ну а как иначе то?

Как иначе? Вызвать execMethod в run

 Я понимаю что так надо сделать! Но мне это не подходит! :)

Цитировать
void execMethod() {}
QFuture<void> QtConcurrent::run (execMethod)

у мну QT 4.1.1 :)

ну вы как не понимаете!

Мне нужно извне запускать методы так, чтобы они выполнялись в другом потоке!

т.е вызываю метод к примеру mWrite(data) - он шлет сигнал в замерший на exec() поток, в котором выполняется слот (например) QIODevice::write(data)

ну и т.п. в том же духе!!! :)


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 05, 2009, 16:59
всё мы понимаем.
и понимаем, что всё это глупо.

1. invokeMethod
2. QCustomEvent
3. шедуль вызова метода через межпотоковую синхронизацию (mutex/semaphore/waiter/etc.)
последний метод удобен для конкурентных вызовов, когда может образовываться "упорядоченная очередь выполнения" (с приоритетами, распараллеливанием и т.д.)

упд.: к тому же, для третьего варианта можно обойтись и без петли (ну, мало ли? - вдруг кто петель боится?) - посмотреть можно в QFileInfoGatherer (или как-то так)...


Название: Re: Опять потоки!
Отправлено: MoPDoBoPoT от Июнь 05, 2009, 17:16
tc->moveToThread(tc)
Поток Клейна прямо какой-то  :)


Название: Re: Опять потоки!
Отправлено: lit-uriy от Июнь 05, 2009, 18:07
Я так понимаю всё это должно появится в "библиотеке для работы с последовательными портами" (http://www.prog.org.ru/topic_9537_0.html)
Только вот "Мне нужно извне запускать методы так, чтобы они выполнялись в другом потоке!" по моему разумению совсем ненужно запускать разные методы. Нужно из потока последовательного порта сигналы слать и всё.


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 18:12
скажите, а если реализовать так как я запостил в этой теме пост №2 - так можно?


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 05, 2009, 18:50
2  lit-uriy,

не, в библиотеке уже ничо нового не будет :) - но суть примерно уловили.. и это только вершина айсберга моей задумки :)

В общем , если говорить по простому, я задумал сделать нечто, использующее и в том числе и эту библиотеку....
Идею объясняю на примере: (только, товарищи программисты просьба абстрагироваться от программирования :) )
1. нужно создать некий класс А, который выполнял бы функцию опроса, записи, и чтения данных из некоего устройства
2. этот класс А для других классов X, Y, Z должен являться "черным ящиком",
3. эти другие классы могут извне управлять классом А (т.е посылать команды) посредством некоего API. Это API пусть представляют из себя два публичных метода класса A
Код:
class A 
{
....
public:
    m_readdata(набор неких параметров);
    m_writedata(набор неких параметров)
}
- эти методы являются по сути просто неким не зависящим от протокола обмена данными интерфейсом. т.е любой сторонний класс чтобы отправить или прочитать данные из класса A просто должен вызвать эти методы.
- эти методы при вызове эмиттяд соответственно сигналы
Код:
class A
{
...
signals:
    sigReaddata(параметры)
    sigWritedata(параметры)
....
}
4. далее.. внутри класса A должна быть запущена нить, в которой с помощью методов класса B (этот класс В  может быть и классом для работы с последова тельными портами и также и сокетом) происходит следующее:
 а) постоянно по таймеру происходит опрос некоего у-ва при помощи методов класса В (например B::read(парам)) и прочитанные данные складываются в некие обрасти памяти класса A
 б) должны отлавливаться сигналы sigReaddata(параметры) и sigWritedata(параметры) и соответственно им вызываться опять же методы класса B::read(параметры) B::write(параметры) которые должны выполняться в нити в которой выполняется и опрос у-ва . Эти методы класса В уже привязаны к определенному протоколу обмена.. т.е форматы передаваемых и приниваемых кадров специфические
----------------------------------------

защищать друг от друга методы B::read(парам)  и B::write(парам) я пока решил мьютексами.

Хочется подчеркнуть, что методы B::read(парам)  и B::write(парам) являются транзакциями, т.е метод B::read(парам) отправляет данные удаленному узлу.. потом ждет пока придет ответ, читает ответ и декодирует его и результат сохраняет в классе A// Аналогично и метод ::write(парам)

В итоге должно получиться так, что методы класса A должны выполняться в своем потоке.. а методы класса В в своем.. связь между ними пока я думаю сигналами сделать...

т.е от методов класса А -> через сигналы -> к методам (слотам) класса В

----------------------

итого, если смотреть мой пост №2 то под классом А можно принять - класс ThreadClass а под классом В - класс PrintClass

В принципе думаю такая идея имеет право на существование.. или может быть посоветуете другую идею как такое реализовать?

 


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 05, 2009, 19:35
Мне вот что не понятно, почему ты все это хочешь запихать в класс-насследник от QThread?
Можно же в самих методах m_readdata и m_writedata создавать и запускать необходимые нити, связывать их с нужными сигналами/слотами.
Всю реализацию скрыть от конечного пользователя за интерфейсом.
IMHO, с точки зрения архитектуры так будет правильней и логичней.


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 06, 2009, 09:47
дополню пост BRE - есть такая вещь как thread pool


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 06, 2009, 15:56
Цитировать
Мне вот что не понятно, почему ты все это хочешь запихать в класс-насследник от QThread?
ну чтобы не городить лишнего :)....
т.е в приват класса А (который наследник QThread)  запихнуть указатель на класс В (в котором имеются публичные методы-слоты для чтения/записи устройства) ... сам класс В создавать в методе run класса А и коннектится там же.. а потом вызвать exec
что-то вроде:

Код:
class A : public QThread
{
    Q_OBJECT
public:
    m_read() { emit s_read(); }
    m_write() { emit s_write(); }
signals:
    s_read();
    s_write();
protected:
    run();
private:
   ClassB *B;
}

A : :run() {
    B = new ClassB;
    connect(this, s_read(), B, slotTransactionRead());
    connect(this, s_write(), B, slotTransactionWrite());
    ....
    exec();
}
....

Цитировать
Можно же в самих методах m_readdata и m_writedata создавать и запускать необходимые нити, связывать их с нужными сигналами/слотами.
например ? код пжлста! :)

Цитировать
дополню пост BRE - есть такая вещь как thread pool
я не нашел такого что-то


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 06, 2009, 16:04
http://doc.trolltech.com/main-snapshot/qthreadpool.html


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 06, 2009, 16:21
Например так:
Код
C++ (Qt)
class A : public QObject
{
   Q_OBJECT
public:
   void m_read()
   void m_write()
 
private:
   ClassB *B;
};
 
void A::m_read()
{
   ThreadRead *th = new ThreadRead();
   connect( th, s_read(), B, slotTransactionRead() );
   th->start();
}
 



Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 06, 2009, 16:40
2 BRE,

1 . но в вашем примере кто будет эмиттить сигнал s_read() ??? и как вообще?
2. зачем городить огород и еще и описывать помимо классов А и В еще и класс th ?
3. не уверен что слот slotTransactionRead выполнится в потоке th . нужно проверить


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 06, 2009, 16:45
2 BRE,

1 . но в вашем примере кто будет эмиттить сигнал s_read() ??? и как вообще?
2. зачем городить огород и еще и описывать помимо классов А и В еще и класс th ?
3. не уверен что слот slotTransactionRead выполнится в потоке th . нужно проверить
Клиент вызывает метод m_read, он запускает отдельную нить (ThreadRead) на чтение данных, когда данные будут готовы, нить посылает сигнал s_read, о том что данные готовы к использованию. Кто будет отлавливать сигнал s_read решает клиент. Данные читаются и готовятся в отдельном потоке.
Опять же клиент может одновременно запустить отдельные нити на чтение и на запись.


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 06, 2009, 17:17
Цитировать
Клиент вызывает метод m_read, он запускает отдельную нить (ThreadRead) на чтение данных, когда данные будут готовы, нить посылает сигнал s_read, о том что данные готовы к использованию.
1. Метод m_read должен содержать параметры...  т.е m_read(параметры) !  Эти параметры должны передаваться в поток (например классу B)
   Как в вашем примере это реализовать?
2. Чтениеданных должны выполнять методы класса В которые должны принять на вход параметры от m_read(параметры) и уже в зависимости от того что за параметры - эти методы будут создавать соответствующие запросы удаленным узлам
3. Мне не нужно чтобы поток емиттил сигналы :)
4. Прочитанные данные поток (методы класса В) должен положить в массивы данных класса А
5. Методы класса В (который выполняется в другом потоке)  должны всегда опрашивать удаленные узлы (по порядку например) если в конфиге узла имеется к примеру флаг ( automaticPoll=true).
6. В методах класса А :  m_read(парам) и m_write(парам) одним из параметров является флаг например manual=true...
- если manual=true, то значит метод m_read(парам) емиттит сигнал в поток , и передает туда параметры - типа какой узел нужно прочитать и т.п. и далее уже читает не сами данные, которые прочитали методы класса B - а данные которы уже готовые хранятся в массивах класса А
- если manual=false - то значит что метод m_read(парам) не будет эммитить сигнал в поток.. а сразу прочитает готовые данные в массивах класса А, т.е подразумевается, что поток и так в автоматическом режиме уже читает эти узлы!  :)

Вот как то так



Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 06, 2009, 18:52
т.е в общих чертах привожу код
вот примерно так должно выглядеть было: (к примеру)

Код:
class B : public QObject
{
Q_OBJECT
public slots:
void slotReadFromNode(int nodeAddress, int startDataAddress, int quantityData) {
/*
1. Читает данные из удаленного узла
2. Сохраняет их в dataNode1 или dataNodeN в зависимости от адреса узла и т.п.
*/
}
void slotWriteToNode(int nodeAddress, int startDataAddress, int quantityData , QByteArray &data) {
/*
выполняется отправка данных в удаленный узел и ожидание положительной квитанции от узла
*/
}
void pollingNodes() {
for (i=0;i<N;i++) { //выполняется опрос всех узлов
slotReadFromNode(int nodeAddress, int startDataAddress, int quantityData, const QByteArray &data);
}
}
public:
QByteArray readReadyData(int nodeAddress, int startDataAddress, int quantityData) {
QByteArray data;
switch (nodeAddress) {
case 1:
data = dataNode1;
//тут из массива data вырезаем кусок от startDataAddress до startDataAddress+quantityData
break;
case N:
data = dataNodeN;
//тут из массива data вырезаем кусок от startDataAddress до startDataAddress+quantityData
break;
}
return data;
}
//массивы готовых данных
QByteArray dataNode1;
QByteArray dataNode2;
.....................
QByteArray dataNodeN;
/*
где:
nodeAddress - адрес запрашиваемого удаленного узла
startDataAddress - стартовый адрес данных которые нужно или прочитать или записать
quantityData - количество байт данных которые нужно записать или прочитать (по порядку)
data - сами данные
*/
}

class A : public QThread
{
Q_OBJECT
signals:
void s_readData(int nodeAddress, int startDataAddress, int quantityData, QByteArray &data);
void s_writeData(int nodeAddress, int startDataAddress, int quantityData, const QByteArray &data);
public:
void m_readData(int nodeAddress, int startDataAddress, int quantityData, QByteArray &data, bool manual) {
if (manual) emit s_readData(nodeAddress, startDataAddress, quantityData, data);
data = cb->readReadyData(nodeAddress, startDataAddress, quantityData);
}
void m_writeData(int nodeAddress, int startDataAddress, int quantityData, const QByteArray &data, bool manual) {
if (manual) emit s_writeData(nodeAddress, startDataAddress, quantityData, data);
}
protected:
void run() {
cb = new B;
connect(this, SIGNAL(s_readData(парам)), cb, SLOT(slotReadFromNode(парам)));
connect(this, SIGNAL(s_writeData(парам)), cb, SLOT(slotWriteToNode(парам)));
QTimer timer;
connect(timer, SIGNAL(timeout()), cb, SLOT(pollingNodes()));
timer->start(1000);
exec();
}
private:
B* cb;
}


вот как то так по идее задумывалось! :)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 07, 2009, 14:10
Я так понял, что ты хочешь примерно такого (псевдокод!):

Код
C++ (Qt)
class B : public QObject
{
Q_OBJECT
public:
void readFromNode(int nodeAddress, int startDataAddress, int quantityData);
void writeToNode(int nodeAddress, int startDataAddress, int quantityData , QByteArray &data);
void pollingNodes();
};
 
class ThreadRead : public QThread
{
Q_OBJECT
public:
ThreadRead( B *cb, int nodeAddress, int startDataAddress, int quantityData ) :
 m_cb( cb ), m_nodeAddress( nodeAddress ), m_startDataAddress( startDataAddress ), m_quantityData( quantityData )
{
}
 
protected:
void run()
{
m_cb->readFromNode( nodeAddress, startDataAddress, quantityData );
}
 
private:
B *m_cb;
int m_nodeAddress;
int m_startDataAddress;
int m_quantityData
}
 
void A::m_readData( int nodeAddress, int startDataAddress, int quantityData, ... )
{
ThreadRead *th = new ThreadRead( cb, nodeAddress, startDataAddress, quantityData );
th->start();
}
 
Объект класса В используется один общий или создается для каждой операции свой?
Класс B должен быть написан с условием того, что его методы могут быть вызваны из разных потоков одновременно!

Мне как-то это все не очень нравиться, по моему нужно еще подумать над архитектурой.  :)
Я не очень понял что должно происходить с флагом manual и что требуется от pooling'a (необходимо его запихнуть в отдельную нить или он может вызываться из главной нити по таймеру?).


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 07, 2009, 17:01
Цитировать
Объект класса В используется один общий или создается для каждой операции свой?
объект класса один общий (например последовательный порт, на котором сидят на RS-485 куча узлов с разными адресами которые нужно опросить)
Цитировать
Класс B должен быть написан с условием того, что его методы могут быть вызваны из разных потоков одновременно!
если использовать твою идею - то думаю никаких проблем не должно возникнуть....  (это в моей идее нереально по моему сделать.. т.е. работать не будет)
а если брать твою идею - то можно создавать 3 разных потока, которые принимают входные параметры как на твоем примере:
1. первый поток - будет вечно зациклен на методе exec() ... в нем постоянно будут опрашиваться все нужные узлы
2. второй поток - запускается если вдруг ПРИНУДИТЕЛЬНО нужно опросить какой-то узел (или группу узлов) извне т.е.
    - если нужно быстрее получить с узла данныене (не ждать очереди его опроса по методу polling ... )
    - если у какого-то устройства в конфиге нет флага "автоматический опрос" (т.е. оно не опрашивается в потоке polling - НО оно есть! :) )
  и по завершении опроса этих узлов (узла) - поток должен автоматически завершится
3. третий поток - запускается если вдруг ПРИНУДИТЕЛЬНО нужно отправить данные в какой то узел (изменить его состояние) 
и по завершении отправки данных и получения положительной квитанции от у-ва  - поток должен автоматически завершится

для того чтобы как-то защитить методы клвсса В от использования их разными потоками одновременно  - думаю просто мьютексом сделать.. при этом мьютекс будет один общий... чтобы если что - то все три потока ждали освобождения этого мьютекса и его использовали

Цитировать
Мне как-то это все не очень нравиться, по моему нужно еще подумать над архитектурой.
да и я тож шото в нерешительности.. собираю пока всю доступную информациююю но, признаюсь, длугой идеи у меня нет пока что

Цитировать
Я не очень понял что должно происходить с флагом manual
ну например:
если какой то внешний класс по какому-то своему алгоритму захочет из класса А получить какие-то готовые данные от узлов то :
 - если узел не имеет в конфиге флага "автоматический опрос" (или еще по какой нить причине)- то внешний класс из класса А вызовет метод с флагом manual (или как нибудь так) - чтобы поток ThreadRead принудительно опросил этот узел ... а потом сам внешний класс просто возмет готовые данные соответствующие этому узлу.... и не беда, что вдруг могут они не обновиться за этот раз.. т.к. в следующие разы они точно обновятся т.к. отработал поток ThreadRead
- если узел ИМЕЕТ в конфиге флаг "автоматический опрос" - то внешний класс читает готовые данные соответствующие узлу прямо из класса А (т.е. не создается поток ThreadRead)

хотя тут у меня еще пока архитектура сумбурная вырисовывается.. но пока что так :)

Цитировать
и что требуется от pooling'a (необходимо его запихнуть в отдельную нить или он может вызываться из главной нити по таймеру?).
требуется "вечно" - пока не прервут или не приостановят - опрашивать все узлы по очереди, имеющие в своем конфиге влаг "автоматический опрос"
должен создаваться в отдельную нить.. и опрос вести по таймеру .. я так думаю .. т.к. иначе не будут сигналы работать.. а они в будующем думаю понадобятся


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 07, 2009, 17:03
Цитировать
Я так понял, что ты хочешь примерно такого (псевдокод!):
дадада.. только еще сюда добавить помимо ThreadRead еще:
1. ThreadWrite
2. ThreaPolling

:)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 07, 2009, 21:58
Почитал я еще раз и возникли вопросы:
1. В каждый конкретный момент времени может выполнятся только одна опереция чтения/записи с/на одно устройство?
2. Интерфейсный класс A представляет своим клиента метод чтения/записи. Например, при чтения с одного устройства данных, этот метод останавливается до тех пор пока все данные не будут получены? Или этот процесс идет асинхронно?

А то как-то вроде все заточено под синхронную передачу, для чего тогда отдельные потоки? Я понимаю еще постоянный опрос устройств, а вот остальное.... ?


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 07:55
Цитировать
1. В каждый конкретный момент времени может выполнятся только одна опереция чтения/записи с/на одно устройство?

если узлы находятся в сети RS-485 (т.е. подключены например к последовательному порту компа, с конвертером USB/RS-485 к примеру) - то в каждый момент времени выполняется одна транзакция. т.е. сначала отсылаются данные узлу.. а потом принимается ответ от узла... и после этого уже идет разбор принятого ответа , и т.д. и т.п. и сохраняется результат в "массивах готовых данных" :) .
т.е защищать мьютексами нужно не по отдельности операции чтения/записи - а транзакцию! (запрос/ответ, запрос/подтверждение и т.п. в зависимости от протокола обмена)

Цитировать
2. Интерфейсный класс A представляет своим клиента метод чтения/записи. Например, при чтения с одного устройства данных, этот метод останавливается до тех пор пока все данные не будут получены? Или этот процесс идет асинхронно?

1. ну, класс А - должен представлять собой самостоятельный объект, который занимается опросом удаленных узлов , сохранения полученных от узлов данных + статусов узлов (т.е например о том что узел недоступен и т.п.)
также класс А для своих "клиентов" просто предоставляет уже готовые данные которые он собрал от узлов.. т.е клиенты из класса А просто запрашивают необходимые им данные...

2. класс не останавливается никогда.. операции автматического опроса узлов ведутся классом непрерывно...  класс А предоставляет своим клиентам ТОЛЬКО методы чтения/записи готовых данных!!! Но не сами методы чтения/записи к узлам! Это уже дело потоков и класса В!
Операции асинхронные... т.е ,еще раз повторю, класс А сам по себе опрашивает узлы и данные о них складывает у себя в массивах(классах/структурах) повторяющих (отображающих) структуру узлов... И для клиентов предоставляется просто интерфейс для  доступа к этой структуре узлов с уже готовыми статусами и данными и т.п.)

т.е класс А будет предоставлять для клиентов иерархическую структуру данных типа:
/
|->канал1 (с параметрами канала)
|        |->узел1 (с параметрами узла)
|        |       |->Данные №1
|        |       |->Данные №2
|        |       |->Данные №N
|        |->узел2 (с параметрами узла)
|                |->Данные №1
|                |->Данные №2
|                |->Данные №N
|

и т.п.

и в конечном итоге чтобы получить  данные №55 от узла №10 в канале №133 этот клиент просто вызовет метод:

А::m_read(Id канала, Id узла, Id Данных, data) = (10, 133, 55)-> и класс А отдаст клиенту готовый кусок содержимого этой структуры (карты сети или как там ее назвать :) )

а если нужно клиенту изменить канале №15 узле №11 данные с №19 то вызовет:
A::m_write (Id канала, Id узла, Id Данных, data) = (15, 11, 19, данные)  и все.. а уже сам класс А создаст поток и использует методы класса В для записи данных в узел..  А запущенный постоянно поток polling потом по-любому обновит структуру отображения (т.е. все-равно в конечном итоге опросит узел в котором данные изменились уже)

Вот так как то нужно бы :)

Цитировать
А то как-то вроде все заточено под синхронную передачу, для чего тогда отдельные потоки? Я понимаю еще постоянный опрос устройств, а вот остальное.... ?
остальное  - это дополнительный поток для записи данных в узел - нужен тоже! т.к. иначе без потока будет подтормаживать интерфейс.. т.к. транзакции имеют такой параметр - как время ожидания ответа от узла (таймаут) т.е если узел вдруг станет недоступен - а таймаут стоит = 5 сек - то .. сами понимаете :)
дополнительный поток - для чтения - это как бы на всякий случай чтобы была такая возможность - принудительно опросить узел...



Название: Re: Опять потоки!
Отправлено: BRE от Июнь 08, 2009, 08:41
Цитировать
А то как-то вроде все заточено под синхронную передачу, для чего тогда отдельные потоки? Я понимаю еще постоянный опрос устройств, а вот остальное.... ?
остальное  - это дополнительный поток для записи данных в узел - нужен тоже! т.к. иначе без потока будет подтормаживать интерфейс.. т.к. транзакции имеют такой параметр - как время ожидания ответа от узла (таймаут) т.е если узел вдруг станет недоступен - а таймаут стоит = 5 сек - то .. сами понимаете :)
дополнительный поток - для чтения - это как бы на всякий случай чтобы была такая возможность - принудительно опросить узел...
Давай посмотрим со стороны клиента.
Есть алгоритм который должен сравнить данные двух узлов:
Код
C++ (Qt)
void func()
{
   Data data1;
   Data data2;
   m_A->readData( 1, 15, 47, data1 );
   m_A->readData( 2, 12, 44, data2 );
   if( data1 != data2 )
       выводим сообщение
}
 
Что будет происходить: первый вызов readData запустит отдельную нить на чтение, основной поток при этом должен быть приостановлен, до тех пор пока данные не будут прочитаны. Потом тоже будет со вторым readData.
Какой смысл запускать эти нити, если основной поток на это время все равно нужно останавливаться (с подтормаживанием GUI)?
Или делать обмен полностью асинхронным, или смысла в нитях для чтения/записи я не пойму.  ???


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 08, 2009, 09:59
я так понимаю, у kuzulis'а предполагается, что на момент readData( 1, 15, 47, &data1 ) уже будут прочитаны данные как минимум до 1, 15, 48 :)
это, конечно, идеализированный вариант. сомневаюсь, что в реальности он будет бессбойно работать xD

а ведь насколько было бы удобнее сделать действительно асинхронную работу, а потом добавить вэйтер, если требуется синхронный вызов :)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 08, 2009, 10:28
я так понимаю, у kuzulis'а предполагается, что на момент readData( 1, 15, 47, &data1 ) уже будут прочитаны данные как минимум до 1, 15, 48 :)
это, конечно, идеализированный вариант. сомневаюсь, что в реальности он будет бессбойно работать xD
По описание вроде бы так, но для чего использовать дополнительный поток для получения готовых данных для меня осталось загадкой.  ;)

а ведь насколько было бы удобнее сделать действительно асинхронную работу, а потом добавить вэйтер, если требуется синхронный вызов :)
+мульон  :)

IMHO, нужно все еще раз хорошо продумать.


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 10:53
Цитировать
Что будет происходить: первый вызов readData запустит отдельную нить на чтение, основной поток при этом должен быть приостановлен, до тех пор пока данные не будут прочитаны. Потом тоже будет со вторым readData.
Какой смысл запускать эти нити, если основной поток на это время все равно нужно останавливаться (с подтормаживанием GUI)?
Или делать обмен полностью асинхронным, или смысла в нитях для чтения/записи я не пойму.  Непонимающий

ненене.. m_A->readData - не будет запускать поток... он тока прочитает готовые уже данные  (это если он был вызван с флагом manual=FALSE)
если же он вызывается с флагом manual=TRUE - то приостонавливается поток polling :) (или какой там сейчай в данный момент работал поток)

Цитировать
я так понимаю, у kuzulis'а предполагается, что на момент readData( 1, 15, 47, &data1 ) уже будут прочитаны данные как минимум до 1, 15, 48 Улыбающийся

данные потоком polling будут прочитаны из ВСЕХ узлов! т.е все данные и з ВСЕХ узлов! (ну, которые автоматически нужно опрашивать в конфиге) :)  и метод m_A->readData - по любому прочитает какие либо данные .. т.е всЁ будет асинхронно происходить!

Цитировать
это, конечно, идеализированный вариант. сомневаюсь, что в реальности он будет бессбойно работать xD
почему? :)

Цитировать
а ведь насколько было бы удобнее сделать действительно асинхронную работу, а потом добавить вэйтер, если требуется синхронный вызов Улыбающийся
ну а сейчас она и есть асинхронная!


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 11:06
Цитировать
По описание вроде бы так, но для чего использовать дополнительный поток для получения готовых данных для меня осталось загадкой.  Подмигивающий
дополнительный поток - для ПРИНУДИТЕЛЬНОГО ЧТЕНИЯ УЗЛА!!! т.е выдать команду классу А на ПРИНУДИТЕЛЬНОЕ ЧТЕНИЕ узла!!!

т.е :
Код:
A::m_A->readData(111,12,1, manual=true, &adata) {
 /*
если флаг=true - то значит нужно принудительно прочитать данный узел (не знаю для чего - но может понадобится)
запускаем поток и забываем про него - т.к. он сам обновит нам структуры данных от узлов
*/
if (manual) запускаем поток ThreadRead;

/*
просто читаем структуру данных
*/
 data = readReadyData (111,12,1);
}

т.е флаг manual нужен чтобы быстрее обновить данные от узла или чтобы их один раз обновить по запросу или если этот узел в конфиге не опрашивается автоматически (в методе polling он игнорируется если например)

и в дальнейшем просто вызывать можно уже A::m_A->readData(111,12,1, manual=FALSE, &adata)  !!!! т.е мы уже знаем что перед этим уже была команда на обновление ! :)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 08, 2009, 11:07
данные потоком polling будут прочитаны из ВСЕХ узлов! т.е все данные и з ВСЕХ узлов! :)  и метод m_A->readData - по любому прочитает какие либо данные .. т.е всЁ будет асинхронно происходить!
Вот смотри, создали объект класс А, он запустил ThreadPool, в которой начали опрашиваться все узлы. Он успел опросить только 3 узла, а пользователь нажал кнопку и затребовал данные с 45 узла, ему просто вернуться пустые данные (с флагом что они не валидные)?

ну а сейчас она и есть асинхронная!
Имеется виду следующий вид протокола. Пользователь делает запрос на чтение данных из какого-то узла, когда эти данные будут готовы, емитится сигнал данные от такого-то узла готовы, можно забирать (или вообще в параметрах сигнала эти данные передать). Этот сигнал и обрабатывается клиентом. Будет ли пре-выборка этих данных или после каждого запроса данные будут читаться из устройства, вопросы реализации...

И все равно, если в один момент времени может происходить только одна транзакция, я не вижу смысла в отдельных потоках чтения/записи.  ;)  ;D


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 08, 2009, 11:10
и в дальнейшем просто вызывать можно уже A::m_A->readData(111,12,1, manual=FALSE, &adata)  !!!! т.е мы уже знаем что перед этим уже была команда на обновление ! :)
А через какое время нужно будет вызвать второй раз readData, как можно быть уверенным, что именно сейчас данные уже получены (нить отработала)?  :)


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 11:15
Цитировать
Вот смотри, создали объект класс А, он запустил ThreadPool, в которой начали опрашиваться все узлы. Он успел опросить только 3 узла, а пользователь нажал кнопку и затребовал данные с 45 узла, ему просто вернуться пустые данные (с флагом что они не валидные)?
дадада.. именно!  т.е у каждого узла помимо данных анализируется еще туева хуча параметров.. таких как статусы! типа :
1. Данные еще не готовы
2. Данные недостоверны
3. и т.п. и. т.д..

:)
но статус "Данные еще не готовы" выставится еще только с самого начала.. но после того как узел хотя-бы раз прочитался - этот флаг(статус) снимется :) ... т.е данные обновляются всякий раз когда в polling происходит чтение этого узла.. и не беда , если метод m_A->readData прочитает старые данные.. т.к. в следующий раз он может прочитать и новые :)

Цитировать
Имеется виду следующий вид протокола. Пользователь делает запрос на чтение данных из какого-то узла, когда эти данные будут готовы, емитится сигнал данные от такого-то узла готовы, можно забирать (или вообще в параметрах сигнала эти данные передать). Этот сигнал и обрабатывается клиентом. Будет ли пре-выборка этих данных или после каждого запроса данные будут читаться из устройства, вопросы реализации...

а вот тут я потом предполагал сделать эммитинье сигналов самим классом А если данные прочитанные в текущий момент например потоком polling отличаются от данных прочитанных им ранее на предыдущем шаге.. и даже думаю ввести параметр типа : "зона нечувствительности", чтобы часто сигнал не эммитился.. и всё ! :)


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 11:19
Цитировать
А через какое время нужно будет вызвать второй раз readData, как можно быть уверенным, что именно сейчас данные уже получены (нить отработала)?  Улыбающийся

а тут выход можно сделать с помошью эммитинья сигналов если данные поменялись!

т.е поток polling эмитит эти сигналы например ВСЕГДА (если данные поменялись)!
и поток ThreadRead эмиттит если данные поменялись (если этот поток был запущен)

т.е можно сделать сразу двумя способами:
1. или ловить сигналы от класса А
2. или просто читать готовые данные

или и то и то в комбинировании


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 08, 2009, 11:35
а вот на это:
И все равно, если в один момент времени может происходить только одна транзакция, я не вижу смысла в отдельных потоках чтения/записи.  ;)  ;D
та и не ответил. получается, что запускаем, скажем, пять потоков; один читает, а остальные в это время ждут; затем второй читает, а первый, третий-пятый ждут. ляпота...

и зачем вообще постоянно сигналить о том, что данные прочитались/обновились? ведь это никого не интересует. в лучшем случае может быть интересным сигнал о том, что запрошенные данные готовы. а остальные?


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 11:45
Цитировать
та и не ответил. получается, что запускаем, скажем, пять потоков; один читает, а остальные в это время ждут; затем второй читает, а первый, третий-пятый ждут. ляпота...

ну да... потоки ThreadRead которые были вызваны методом A::m_A->readData(111,12,1, manual=true, &data) или
потоки ThreadWrite вызванные A::m_A->writeData(111,12,1, const &data)сделают свое дело и завершатся! а поток polling останется в работе "вечно" :)

И иначе то никак нельзя поступить.. т.к. ресурс - допустим последовательный порт - он то один! :)  И за одну транзакцию необходимо сделать запрос/ответ !!!

Цитировать
и зачем вообще постоянно сигналить о том, что данные прочитались/обновились?

не обновились - а ИЗМЕНИЛИСЬ! на какую то величину.. например читая такой параметр как "Напряжение" если "зона" стоит = 1 Вольт, прочитали 220 Вольт, а до этого было 221 вольт  - то съэмиттится сигнал со значением = 220 Вольт и обновится значение в структуре

и если что - можно "прицепится" или к сигналу.. или "тупо" читать структуру

Цитировать
ведь это никого не интересует. в лучшем случае может быть интересным сигнал о том, что запрошенные данные готовы. а остальные?

нет нет нет! Сигнал о том что данные изменились!!!!! с указанием ID канала, ID узла, ID данных и их значением!


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 11:59
Цитировать
ведь это никого не интересует.
Это интересует ЯДРО моей "системы"...

Ядро имеет массивы объектов типа "точка" ...
"Точка" - имеет такие параметры как:
1. UID - уникальный идентификатор
2. Имя - текстовая строка (например "Мощность на фазе А")
3. Описание - текстовая строка (например "Мощность на фидере №1")
4. Тип данных - (Int, bool, float и т.п.)
5. Ссылка на источник данных - это кодированная ссылка например на объекты класса А , т.е по этой ссылке из класса А можно получить данные
(например Link = P_ID, CH_ID, NODE_ID, DATA_ID
где
P_ID - идентификатор протокола (класса А)
CH_ID - ид. канала в классе (объекте, протоколе и т.п.)
NODE_ID - ид. узла в канале
DATA_ID - ид. данных в узле
...
..

Ядро с этой ТОЧКОЙ производит различные манипуляции (обрабатывает её значение, пересчитывает диапазоны, уставки, алармы, варнинги и т.п и. т.д )
т.е. клиентом класса А является ЯДРО! А класс А - для ядра - просто источник данных для точек ядра ! :)


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 08, 2009, 13:45
kuzulis, прекращай постить мессаги один за другим - создаётся впечатление, что сам себе что-то доказываешь...

ну да... потоки ThreadRead которые были вызваны методом A::m_A->readData(111,12,1, manual=true, &data) или
потоки ThreadWrite вызванные A::m_A->writeData(111,12,1, const &data)сделают свое дело и завершатся! а поток polling останется в работе "вечно" :)
всё это мог бы сделать и один поток (например, "поток polling"). если у тебя непреодолимое ограничение - последовательный доступ к данным, какой смысл плодить "сонные" потоки, когда есть такая замечательная штука как очередь?


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 14:22
Я разобраться пытаюсь.. и ничего не доказываю :(


Цитировать
когда есть такая замечательная штука как очередь?

ну например как это с очередью сделать? конкретный примерчик пжлста в коде (набросок)


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 08, 2009, 14:41
я ведь предлагал уже обратить внимание на файлинфогэзерер - там и очередь, и асинхронный возврат результата, и даже потокобезопасность уже проработана - бери-не хочу!


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 15:32
это тут ?

http://doc.trolltech.com/main-snapshot/qthreadpool.html


Название: Re: Опять потоки!
Отправлено: ритт от Июнь 08, 2009, 16:08
его в доках нет, оно есть в сорцах.


Название: Re: Опять потоки!
Отправлено: kuzulis от Июнь 08, 2009, 17:40
где? так и искать? фарлингфреххренессер? :)


Название: Re: Опять потоки!
Отправлено: BRE от Июнь 08, 2009, 18:13
где? так и искать? фарлингфреххренессер? :)
;D
QFileInfoGatherer