Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Ввод/Вывод, Сеть. Межпроцессное взаимодействие _ Помогите разобраться c передачей данных по сети!

Автор: PVGDRK 24.9.2010, 15:35

Имеется программа, которая выполняет следующие действия,
динамически выделяется оперативная память для хранения очень большого массива данных далее эти данные по сети посылаются в другой компутер,,, При передаче данных по сети вся система начинает тормозить. программу невозможно закрыть до тех пор пока не закончится эта передача .,,,
В общем посоветовали мне передачу данных организовать не в основном потоке...
поскольку опыта программирования у меня почти нет - приходиться учиться по ходу дела буду всем признательна за помощь
поможет ли избавиться от "тормозов" организация многопоточности - может есть какие то решения по-проще?
Далее вопрос про сам принцип организации этой многопоточности
в книге пишут,что нужно создать класс QThread и перезаписать в нем виртуальный метод run(), в котором должен быть реализован код, который будет исполняться в потоке

class MyThread:public QThread
{
   public:
   void run()
    {
      код исполняемый в потоке
     }
}

Суть вроде ясна, а вот как переменные и соединения сигнал-слот из базового класса перенести в этот метод run()?
Может я не совсем корректно что то написала.т.к. не разбираюсь в данном вопросе.... сама программа передачи данных была написана другим человеком на С , мне лишь нужно было сделать для нее графический интерфейс в QT и кое чего добавть...
помогите пожалуйста разобраться в данном вопросе

Автор: Алексей1153 24.9.2010, 18:34

Цитата(PVGDRK @ 24.9.2010, 18:35) *
поможет ли избавиться от "тормозов" организация многопоточности

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

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

Автор: PVGDRK 24.9.2010, 19:09

Спасибо за разъяснения. Я примерно так и предполагала.
А вот как быть с самой реализацией второго потока?
Почитала про дружественные функции - может это поможет объединить объекты двух классов, или делать дружественные классы? Или я ошибаюсь?

Автор: Алексей1153 24.9.2010, 19:21

Цитата(PVGDRK @ 24.9.2010, 22:09) *
А вот как быть с самой реализацией второго потока

так у тебя же в посте приведён таки пример реализации - там только осталось наполнить процедуру потока (run) :)

Автор: PVGDRK 24.9.2010, 20:12

Это мне понятно, а как в этом классе QThread использовать мой массив, к которому я обращаюсь в базовом классе через указатель? Имеет ли значение то,что массив хранится в оперативной памяти и используется еще и в других функциях базового класса? Как сделать, чтобы остальные переменные были "видны" в классе QThread с теми же значениями, которые они принимают в базовом классе?
Прошу прощения за глупые вопросы, мне еще никогда не приходилось работать с двумя классами :unsure:

Автор: Алексей1153 24.9.2010, 20:19

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

Автор: PVGDRK 24.9.2010, 20:28

Ох, спасибо :) Про переменные почитаю - спасибо ,что сказали, как они называются. А вот про синхронизацию в ООП впервые слышу :blink:
Как то до этого больше с железяками возилась :unsure:

Автор: Алексей1153 24.9.2010, 20:38

PVGDRK, железки - обычно однопоточные, поэтому в них про синхронизацию не задумываешься )

про переменные - будет нечто вроде

//это некий класс, где храниться массив и так далее
class CMyClass
{

};


class MyThread:public QThread
{
   CMyClass* m_pDataKeeper;
  

   public:
   MyThread(CMyClass* m_pDataKeeper=0):m_pDataKeeper(m_pDataKeeper)
   {
   }

//тут ещё, может быть, методы
//....

   void run()
    {
        if(m_pDataKeeper)
        {
              m_pDataKeeper->....;
         }
      код исполняемый в потоке
     }
}


Цитата(PVGDRK @ 24.9.2010, 23:28) *
А вот про синхронизацию в ООП впервые слышу

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

Автор: PVGDRK 24.9.2010, 23:29

Спасибо большое за разъяснения. В понедельник буду разбираться дальше :)

Автор: Алексей1153 24.9.2010, 23:41

а в выходные не будешь ? )

Автор: PVGDRK 25.9.2010, 18:19

Не , в выходные не получится - нет сети дома, да и компа того нет, он специфический, да и данные тоже специфические :)

Автор: Гость_xls_* 27.9.2010, 9:43

Почитайте http://doc.qt.nokia.com/qq/qq27-responsive-guis.html

Автор: PVGDRK 27.9.2010, 10:15

Mercie beaucoup :) ( Большое спасибо ) Заодно и английский подучу :)

Автор: Алексей1153 27.9.2010, 10:42

Цитата(PVGDRK @ 27.9.2010, 13:15) *
Заодно и английский подучу

и это правильно )))

Автор: PVGDRK 30.9.2010, 15:35

Простите меня бестолковую, пожалуйста, помогите понять что к чему (я еще и сам С++ практически не знаю) :unsure:
Написала так как Вы тут привели в примере - на место фразы "код,исполняемый в потоке" вставляю свой код с переменными из класса СMyClass - компилятор ругается пишет типа неизвестная переменная...
Не могли бы Вы мне помочь понять что к чему?
Что означает запись в Вашем примере,

MyThread(CMyClass* m_pDataKeeper=0):m_pDataKeeper(m_pDataKeeper)
   {
   }

 if(m_pDataKeeper)
        {
              m_pDataKeeper->....;  
         }

строчка m_pDataKeeper->...служит для того, чтобы обратиться к какому либо элементу моего базового класса CMyClass? при вводе знака -> выскакивает список методов (не знаю как правильно назвать) но там максимум присутсвуют только описанные в классе CMyClass функции но не переменные...
как работает эта конструкция if(m_pDataKeeper)? Каким образом из основного потока в классе CMyClass попадаем в это место?
что происходит при написании строки m_pDataKeeper->....; и что пишется после знака ->?

и еще я не совсем понимаю что где писать :unsure:

CMyClass - это мой класс, где хранятся мои переменные массивы данных и функции и используются сигналы и слоты. Этот класс является базовым и все что там выполняется - это выполняется в основном потоке? Я правильно понимаю?
Этот класс имеет три проектных файла в которых я что то делала:
CMyClass.ui - форма, куда помещаются различные виджеты
CMyClass.h - где содержится описание класcа , прототипы функций и некотороые переменные
и файл CMyClass.сср - где иммется конструктор класса, деструктор, и уже пишется сам код программы.
Если создавать класс QThread - нужно ли создавать для его описания отдельные h и cpp файлы ?
Почему приведенный Вами код нормально скомпилировался, если его разместить в файле CMyClass.cpp в самом конце (только если не писать свой код с исползованием переменных из класса CMyClass ) и почему то ничего не получилось, если я пыталась сделать для класса QThread свои h и cpp файлы в инклюдах вроде везде все что нужно написала ...
Благодарю за помощь в познании

Автор: Алексей1153 1.10.2010, 6:27

Цитата(PVGDRK @ 30.9.2010, 18:35) *
Простите меня бестолковую

для начала - прекрати себя бестолковой считать!

Цитата(PVGDRK @ 30.9.2010, 18:35) *
вставляю свой код с переменными из класса СMyClass - компилятор ругается пишет типа неизвестная переменная...

покажи код то :)

Цитата(PVGDRK @ 30.9.2010, 18:35) *
Что означает запись в Вашем примере,
MyThread(CMyClass* m_pDataKeeper=0):m_pDataKeeper(m_pDataKeeper)
{
}

после двоеточия - список инициализаторов членов класса. В данном случае список состоит из одного элемента - m_pDataKeeper, в его конструктор передаётся параметр, переданный через кенструктор класса MyThread

я просто назвал параметр так же, как и член класса, тут я был неправ в плане наглядности. Вот так будет понятнее:
MyThread(CMyClass* pDK=0):m_pDataKeeper(pDK)
{
}


Цитата(PVGDRK @ 30.9.2010, 18:35) *
строчка m_pDataKeeper->...служит для того, чтобы обратиться к какому либо элементу моего базового класса CMyClass?


опкратор "->" позволяет обратиться к члену экземпляра (или функции) класса по указателю на экземпляр

Цитата(PVGDRK @ 30.9.2010, 18:35) *
CMyClass - это мой класс, где хранятся мои переменные массивы данных и функции и используются сигналы и слоты. Этот класс является базовым и все что там выполняется - это выполняется в основном потоке? Я правильно понимаю?

вообще-то, он не обязательно базовый (базовым классом называют класс-предок)

Точнее сказать: экземпляр класса CMyClass создан в основном потоке. И по совместительству, как понимаю, этот класс у тебя - класс главного окна ?

Автор: PVGDRK 1.10.2010, 11:59

Цитата
Точнее сказать: экземпляр класса CMyClass создан в основном потоке. И по совместительству, как понимаю, этот класс у тебя - класс главного окна ?

Да так и есть. только у меня в программе он называется не CMyClass а Generator, но суть от этого не меняется...
Итак, сам код: файл generator.cpp
extern "C" __declspec (dllimport) int Create30000000(WaveformDataReal **bS); //  Функции написанные в DLL файле
extern "C" __declspec (dllimport) int Add30000000(FILE *fin,float freq, float SNR);

Generator::Generator(QWidget *parent, Qt::WFlags flags)   // Конструктор
    : QMainWindow(parent, flags)
{
    ui.setupUi(this);
    createFilesTable();
    Disable();
}
Generator::~Generator()                                    // Деструктор
{
    CloseAWG();
}
//Далее идут  функции  и обработчики соединения сигнал слот
.......
//Один  из них:

void Generator::on_pushButton_5_clicked()
{
     int t=0;
     int n=0;
     int p=0;
     int x1,x2,y1,y2;
     QString str_filename;
     QString str_txt;
     QByteArray ar;
     char* datatext;
     char str[128];
     WaveformDataReal *bS; //  bS - это  тот  массив,который находится в оперативной памяти
     float freq, SNR;
     FILE *F_1;
     bool ok;
     float Value=0;
,,,,,,
// Далее как я понимаю  идет  функция формирования этого  массива путем обращения к
// Create30000000(&bS) и Add30000000(F_1, freq, SNR)
// Далее данные из  сформированного  массива можно либо отправить  по  сети, либо  сохранить  в файл...
// Я пока пытаюсь  в потоке QThread выполнить  сохранение в файл  поэтому  вот  этот  код вставляю  в потоке QThread

if(ui.checkBox_File->checkState()==Qt::Checked)   // Запись  в файл
        {
            if(pPBar!=NULL) pPBar->setWindowTitle(tr("Write file"));

            FILE *F_2;
            str_txt = ui.lineEdit_NameF->text();
            ar = str_txt.toAscii();
            datatext=ar.data();
            F_2=fopen(datatext,"wb");
            for(int j=0;j<100;j++)
            {
                fwrite((bS+j*300000),sizeof(WaveformDataReal),300000,F_2);
                if(pPBar!=NULL)    pPBar->setValue(j);
                Sleep(10);
            }
            fclose(F_2);
        }
}
//Далее пишу

class MyThread:public QThread
{
    Generator*m_pData;
    
public:
    MyThread(Generator*m_pData=0):m_pData(m_pData) // Здесь  очевидно  ошибка из-за моего непонимания m_pData - указатель н а мой класс  или какая то переменная или функция из  класса Generator?
    {
    }
    void run()           //Если в этой функции  не писать  ниже приведенный код - то  все компилируется,  а скодом ругается на переменные что они не определены ...  а как правильно  все это  пишется я не знаю
    {
        if(m_pData)
        {
            m_pData-> // здесь не совсем поняла,что нужно писать поэтому ничего не писала
        }

        if(ui.checkBox_File->checkState()==Qt::Checked)   // Запись  в файл
        {
            if(pPBar!=NULL) pPBar->setWindowTitle(tr("Write file"));
            
            FILE *F_2;
            str_txt = ui.lineEdit_NameF->text();
            ar = str_txt.toAscii();
            datatext=ar.data();
            F_2=fopen(datatext,"wb");
            for(int j=0;j<100;j++)
            {
                fwrite((bS+j*300000),sizeof(WaveformDataReal),300000,F_2);
                if(pPBar!=NULL)    pPBar->setValue(j);
                Sleep(10);
            }
            fclose(F_2);
        }
        }

Вот примерно так....
Подскажите,пожалуйста, как правильно написать :unsure:

Автор: Алексей1153 1.10.2010, 12:08

а где у тебя экземпляр MyThread создаётся ?

по меньшей мере к ui доступ будет так

m_pData->ui

Автор: PVGDRK 1.10.2010, 12:36

Да к ui доступ есть псоле записи ui. выпадает список всего того. что размещено у меня на форме и есть еще какието setupUi b retranslateUi....

Автор: Алексей1153 1.10.2010, 12:38

PVGDRK, так, стоп! "какие-то" - это именно принадлежащие ui.

Всё-таки, ответь на вопрос

Цитата(Алексей1153 @ 1.10.2010, 15:08) *
а где у тебя экземпляр MyThread создаётся ?


по идее, это должно происходить в конструкторе твоего Generator

Автор: PVGDRK 1.10.2010, 12:57

Не, я так понимаю совсем не в конструкторе моего генератора если посмотреть на выше приведенный код , то экземпляр MyThread создаётся где то в самом низу в файле generator.cpp....
Я правильно понимаю. что экземпляр MyThread нужно создать вот здесь?

Файл generator.cpp

Generator::Generator(QWidget *parent, Qt::WFlags flags)   /[u][b]/ Конструктор[/b][/u]
    : QMainWindow(parent, flags)
{
    ui.setupUi(this);
    createFilesTable();
    Disable();
}
Generator::~Generator()                                    // Деструктор
{
    CloseAWG();
}

или в h файле?

#ifndef GENERATOR_H
#define GENERATOR_H

#include....
......
class Generator : public QMainWindow
{
    Q_OBJECT        
public:
    Generator(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~Generator();

    QString data[100];  
    ViSession    rm, vi;
    ViStatus    status;
    char buffer_1[BUFSIZ];
    ViUInt32    retCount;
    bool OpenAWG;
    
public:
    Ui::GeneratorClass ui;
    QTableWidget *filesTable;
    void CloseAWG(void);
    void ReadWaveFormData(void);
    void ReadSetting(void);
    void Enable(void);
    void Disable(void);

public slots:
    void createFilesTable();
    void on_pushButton_5_clicked();
    void on_pushButton_3_clicked();
    void on_tableWidget_cellDoubleClicked(int Col, int Row);
    void on_pushButton_2_clicked();    

private slots:
    void on_tabWidget_currentChanged(int);
    void on_comboBox_2_textChanged(const QString &);
    void on_lineEdit_NameF_textChanged(const QString &);
    void on_pushButton_8_clicked();
    void on_comboBox_textChanged(const QString &);
    void on_pushButton_4_clicked();
    void on_pushButton_7_clicked();
    void on_lcdNumber_overflow();
    void on_pushButton_6_clicked();
    void on_pushButton_clicked();    
};
#endif // GENERATOR_H

Автор: Алексей1153 1.10.2010, 13:05

PVGDRK, поиском я не нашёл, где у тебя создаётся экземпляр MyThread. Может, ты не весь код показала ? :)

Или "внизу файла" - это ты говоришь про ОПИСАНИЕ класса

class MyThread:public QThread
{

?

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

Автор: PVGDRK 1.10.2010, 13:14

Я так понимаю у меня вообще не создается экземпляр класса QThread :unsure:
Проект в личку отправлю :wub:

Автор: Алексей1153 1.10.2010, 13:22

Цитата(PVGDRK @ 1.10.2010, 16:14) *
Я так понимаю у меня вообще не создается экземпляр класса QThread

правильно )

Цитата(PVGDRK @ 1.10.2010, 16:14) *
Проект в личку отправлю

зачем, прикрепи к посту. Всё секретное и эротическое - вырезай ))

Автор: PVGDRK 1.10.2010, 13:34

Если я все вырежу - то Вы мне тогда не сможете помочь :)
Эх, у нас тут на работе настройки инета такие, что мне ни сюда ни в личку ничего не загрузить :unsure:

Автор: Алексей1153 1.10.2010, 13:36

PVGDRK, ладно, щас набросаю схемку. Но! Срочно читать книжки по C++!!! Про классы.

Автор: PVGDRK 1.10.2010, 13:39

Так я читаю - видно плохо понимаю что пишут... в книге 850 страниц я пока на 44 :(

Автор: Алексей1153 1.10.2010, 13:47

файл MyThread.h

Раскрывающийся текст
#pragma once
#include <QThread>
class Generator;

class MyThread:public QThread
{
    Generator* m_pData;
    
public:
    MyThread(Generator* pData=0);
    virtual void run();
};




файл MyThread.cpp
Раскрывающийся текст
#include "MyThread"

MyThread::MyThread(Generator* pData)
       :m_pData(pData)
{
}

void MyThread::run()
{
}



файл Generator.h
Раскрывающийся текст
#pragma once
#include <QMainWindow>
class MyThread;

class Generator : public QMainWindow
{
    Q_OBJECT        

    MyThread* m_pTHREAD;
public:
    Generator(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~Generator();
...
...
};



файл Generator.cpp
Раскрывающийся текст
#include "Generator"
#include "MyThread"

Generator::Generator(QWidget *parent, Qt::WFlags flags)
    : QMainWindow(parent, flags)
{
    ui.setupUi(this);
    createFilesTable();
    Disable();

   m_pTHREAD=new MyThread(this);
}



Цитата(PVGDRK @ 1.10.2010, 16:39) *
в книге 850 страниц я пока на 44

про классы в книгах по C++ обычно пишут в самом начале - потому что это основы

Автор: PVGDRK 1.10.2010, 14:08

Безмерно Вам благодарна за подробный ответ :give_rose:
Нечто подобное я пыталась написать , но видимо что то не так сделала и не помогло, а потом вообще запуталась что где пишется и в каких файлах - в моей книжке про это ничего не пишут... вот решила обратиться за помощью на форум...
Спасибо Вам большое еще раз. попробую сейчас реализовать это

Автор: PVGDRK 6.10.2010, 11:07

Помогите теперь, пожалуйста понять, как это работает ... :unknown:
с горем по-полам что то написала
в файле main.cpp дописала пару строк:

MyThread thread;
    thread.start();

в файле
MyThread.cpp в методе run() дописала вызов своей функции из класса Generator
Раскрывающийся текст
void MyThread::run()
{
m_pData->on_pushButton_5_clicked();
}


Решила посмотреть как это работает в пошаговом режиме...
Получается.что как только программа запускается я попадаю на точку останова с вызовом функции m_pData->on_pushButton_5_clicked(); и сразу же перехожу к ее выполнению . получается.что у меня даже главное окно программы не успевает загрузиться
мне кажется,что перед этой строкой нужно написать что то тииа
if (m_pData-> что  тут не знаю )

Но как тогда из основного потока при нажатии кнопки pushButton_5_clicked() выполнить действия в другом потоке?
Если написать просто:
if (m_pData)
{
m_pData->on_pushButton_5_clicked();
}

то функция m_pData->on_pushButton_5_clicked(); не вызыается и тогда. как я понимаю, все делается в основном потоке ?

Автор: Алексей1153 6.10.2010, 11:18

Цитата(PVGDRK @ 6.10.2010, 14:07) *
thread.start();

это напиши в конце конструктора главного окна. Или после созданияокна в main. Тогда окно успеет создасться :)

про on_pushButton_5_clicked я не совсем понял. Поток должен по неким сигналам понять, что нужно выполнить некую функцию - вот эти сигналы и надо приделать (сигналы тут - не прямо signals, а в общем смысле)

Автор: PVGDRK 6.10.2010, 12:05

Да при нажатии на кнопку pushButton_5 в другом потоке должны выполниться определенные действия,
я пока тупо все что делается в обработчике сигнала on_pushButton_5_clicked(); хочу сделать в другом потоке, т,к. не совсем понимаю как выполнить часть действий в частности потому.что не знаю как обратиться к некотороым функциям, которые у меня импортировааны из dll....

Раскрывающийся текст

extern "C" __declspec (dllimport) int Create30000000(WaveformDataReal **bS);
extern "C" __declspec (dllimport) int Add30000000(FILE *fin,float freq, float SNR);

как к ним можно обратиться в файле MyThread.cpp?
Если я пишу m_pData -> дальше выпадает список используемых мной функций в классе генератор но этих, которые из dll нету в списке.
И еще вопрос у меня в обработчике сигнала on_pushButton_5_clicked() есть локальные переменные к которым через m_pData не обратиться - какие есть варианты решения проблемы - сделать их глобальными или еще как то можно сделать?

Автор: Алексей1153 6.10.2010, 12:33

Цитата(PVGDRK @ 6.10.2010, 15:05) *
не знаю как обратиться к некотороым функциям, которые у меня импортировааны из dll....

так ты их в cpp-файле потока тоже задекларируй, их тоже будет видно :)

Цитата(PVGDRK @ 6.10.2010, 15:05) *
есть локальные переменные к которым через m_pData не обратиться

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

Автор: PVGDRK 6.10.2010, 15:08

так они у меня и так в cpp файле генератора описаны. но до конструктора ...
а в каком виде их нужно писать в файле потока?

int Create30000000(WaveformDataReal **bS)
{   а тут  тогда что  писать?
}


Автор: Алексей1153 6.10.2010, 17:42

PVGDRK, "до конструктора" - это, я так понимаю, на твоём языке означает "не член класса и расположен в cpp-файле" :)

тут выхода по меньшей мере два:
1) если не хочешь из cpp убирать, сделай в Generator функцию-трамплин.
2) или же сделай Create30000000 статической функцией класса Generator

Автор: PVGDRK 7.10.2010, 8:18

Спасибо Вам добрый человек за терпение и внимание к моим дурацким вопросам
Испытываю непередвавемые ощущения от осознания того, что нормальные люди пишут это минут за 15 а я за 2 недели и то не могу написать :cray:

Автор: Алексей1153 7.10.2010, 19:25

PVGDRK, я на самом деле злой, я ж из Обливиона ))

А скорость разработки - это всего лишь опыт и количество практики. Придёт и к тебе :) Ты достаточно упорная для этого

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)