Нужна помощь профессионального программиста

 

Привет!

Есть MDI приложение, которое получает данные по DDE

Дочерних окон  много (сейчас 99), но может быть и больше (около 140)

Из главного окна данные передаются в дочернее окно так:

//--- Find Expert in child window ---
            for j:= 0 to MainForm.MDIChildCount - 1 do
            begin
              Child:= TMDIChild(MainForm.MDIChildren[j]);
              if(Child <> nil) then
              begin
                for k:= 0 to Data.ColCount - 1 do
                begin
                  if(Child.Expert.SpotName = Data.Cells[0, k]) then   //found child
                  begin
                    if(CreateOutData(Child.Expert.FutName, Child.Expert.SpotName) = true) then // prepare out dada
                    begin
                      Child.OnData(@OutData);  //Send data
                      PostMessage(Child.Expert.EHandle, WM_ON_TICK, Child.Expert.EHandle, 0);  //Init OnTick
                    end;
                    break;
                  end;
                end;
              end;
            end;

 В дочернем окне данные принимаются

procedure TMDIChild.OnData;
var
  done: boolean;
begin
  FExpert.FinData:= TTransData(Data^); //resive data
  Mutex.Lock;
  try
    done:= Expert.TransBusy;  //Ckeck transaction in progress
  finally
    Mutex.Unlock;
  end;
  if(done = false) then FExpert.UpdExpertData(); //Uрdate expert data
end;

OutData в главном окне и inData в дочернем окне объявлены как структура (Transfer data)

//--- Topic data ---
  TTopicDada = packed record
    Name: string;
    Cells: array[0..1]of array of string;
  end;

//---Transfer data ---
  PTransData = ^TTransData;
  TTransData = packed record
    FutData: TTopicDada;
    SpotData: TTopicDada;
    DepoData: TTopicDada;
  end;

 Для развязки окон используется

PostMessage(Child.Expert.EHandle, WM_ON_TICK, Child.Expert.EHandle, 0);  //Init OnTick

Все нормально работает, но достаточно медленно

Идет непрерывный поток данных по DDE, а дочерних окон много

и пока в дочернем окне не отработает процедура 

UpdExpertData();

Процесс раздачи данных по дочерним окнам приостанавливается

Если в дочернем окне убрать процедуру 

UpdExpertData();

А по PoatMessage вызвать её, то во входных данных уже появляются данные, предназначенные другому окну

Сам вопрос:

Как сделать, чтобы данные просто передались в дочернее окно, а уж потом, по PostMessage, они парсились? 

 
prostotrader:

Привет!

Есть MDI приложение, которое получает данные по DDE

Дочерних окон  много (сейчас 99), но может быть и больше (около 140)

Из главного окна данные передаются в дочернее окно так:

 В дочернем окне данные принимаются

OutData в главном окне и inData в дочернем окне объявлены как структура (Transfer data)

 Для развязки окон используется

Все нормально работает, но достаточно медленно

Идет непрерывный поток данных по DDE, а дочерних окон много

и пока в дочернем окне не отработает процедура 

Процесс раздачи данных по дочерним окнам приостанавливается

Если в дочернем окне убрать процедуру 

А по PoatMessage вызвать её, то во входных данных уже появляются данные, предназначенные другому окну

Сам вопрос:

Как сделать, чтобы данные просто передались в дочернее окно, а уж потом, по PostMessage, они парсились? 

Можно для каждого дочернего окна создавать отдельный поток и UpdExpertData(); запускать в отдельном потоке. PostMessage в отличии от SendMessage вроде асинхронная функция и во время ее работы основной поток не останавливается. Надо еще придумать какие то флаги сигнализирующие о том, что процесс в процессе :) обработки данных и пока он его не отработает не передавать новые данные этому окну. Если каждое дочернее окно будет работать в отдельном потоке, то тогда процесс передачи данных не будет останавливаться и ждать пока окно отработает данные, а сразу же будет передавать данные следующему окну. Слабое место всех MDI приложений, как раз в этом что все дочерние окна работают в одном потоке главного окна. Если для каждого дочернего окна предусмотреть отдельный поток то думаю проблема будет решена.

...

Вот накопал статейку на эту тему. https://www.ispras.ru/proceedings/docs/2017/29/1/isp_29_2017_1_231.pdf

Вот еще - https://habr.com/ru/post/144681/


....

Еще вопрос в процессе работы используете функцию  Application.ProcessMessages?

Библиотека OmniThreadLibrary — простая многопоточность в среде Delphi
Библиотека OmniThreadLibrary — простая многопоточность в среде Delphi
  • 2012.05.28
  • habr.com
Написать интересную статью на техническую тему очень сложно. Приходится балансировать между тем, чтобы не скатиться в технические дебри и тем, чтобы совсем ничего не сказать. Сегодня я попробую в общих словах (без деталей) поговорить о том, как обстоят дела с разработкой многопоточных desktop-приложений в не столь популярной на сегодняшний...
 
Vitalii Ananev #:

Можно для каждого дочернего окна создавать отдельный поток и UpdExpertData(); запускать в отдельном потоке. PostMessage в отличии от SendMessage вроде асинхронная функция и во время ее работы основной поток не останавливается. Надо еще придумать какие то флаги сигнализирующие о том, что процесс в процессе :) обработки данных и пока он его не отработает не передавать новые данные этому окну. Если каждое дочернее окно будет работать в отдельном потоке, то тогда процесс передачи данных не будет останавливаться и ждать пока окно отработает данные, а сразу же будет передавать данные следующему окну. Слабое место всех MDI приложений, как раз в этом что все дочерние окна работают в одном потоке главного окна. Если для каждого дочернего окна предусмотреть отдельный поток то думаю проблема будет решена.

...

Вот накопал статейку на эту тему. https://www.ispras.ru/proceedings/docs/2017/29/1/isp_29_2017_1_231.pdf

Вот еще - https://habr.com/ru/post/144681/


....

Еще вопрос в процессе работы используете функцию  Application.ProcessMessages?

Спасибо, конечно, но Вы вероятно не поняли.

Дочернее окно лишь окно, в котором отображается информация, в всю работу выполняет класс

type
  TExpert = class
    FFutName: string;
    FSpotName: string;
    FFutAccaunt: string;
    FSpotAccaunt: string;
    FFXAccaunt: string;
    FClient: string;
    FTransBusy: boolean;
    FOrder: EntityNumber;
    FTransID: DWord;
    FExpData: TExpertData;
    FEHandle: THandle;
    FMaxValue: Double;
    FMinValue: Double;
    FTransAction: TTRansID;
    FStopTrading: boolean;
    FMemo: TMemo;
    FCEntLabel: TLabel;
    FСExitLabel: TLabel;
    FFutLabel: TLabel;
    FSpotLabel: TLabel;
    FMaxLabel: TLabel;
    FMinLabel: TLabel;
    FCBLabel: TLabel;
    FIntLabel: TLabel;
    FExtLabel: TLabel;
    FDivLabel: TLabel;
    FAvlLabel: TLabel;
    FMCntLabel: TLabel;
    FDeltaLabel: TLabel;
    FCurDivLabel: TLabel;
    FSettings: TExpertSettings;
    FFutVol: double;
    FSpotVol: double;
    FInData: TTransData;
    FOrderPrice: Double;
    FTrRes: Long;
    FExCode: Long;
    FTCode: Long;
    FRepMess: LPSTR;
    FVolume: Quantity;
    FaSell: Long;
    FMustSpotVol: double;
    FDllConn: boolean;
    FQConn: boolean;
    FWrongData: TWrongData;
    FEntEdit: TEdit;
    FExitEdit: TEdit;
    private
      procedure OnTick();
      procedure UpdExpertData();
      function CheckTime(var is_rem_order: boolean): boolean;
      function CalcProc(var inVal, outVal, CBuy, aDelta, Cdivs, iSell, oBuy: double): boolean;
      function PositionState(): POS_STATE;
      function GetFutInVolume(const aVol: double): double;
      function CheckMoney(const vol: double): double;
      procedure SendOrder(const dir: integer; const price: double; const qnty: double);
      function GetTransID(): dword;
      function CheckPosEqual(): double;
      function GetFutOutVolume(const aVol: double): double;
      procedure SellBuySpot(const aVol: double; const BuySell: integer);
      function CheckFutPrice(const aPrice: double): boolean;
    public
      constructor Create();
      destructor Destroy; override;
      procedure OnTrade();
      procedure OnError();
   //   procedure OnOrder();
//--- Property
      property FutName: string read FFutName;
      property SpotName: string read FSpotName;
      property FutAccaunt: string read FFutAccaunt;
      property SpotAccaunt: string read FSpotAccaunt;
      property FXAccaunt: string read FFXAccaunt;
      property Client: string read FClient;
      property TransBusy: boolean read FTransBusy;
      property Order: EntityNumber read FOrder;
      property TransID: DWord read FTransID;
      property ExpData: TExpertData read FExpData;
      property EHandle: THandle read FEHandle;
      property MaxValue: Double read FMaxValue;
      property MinValue: Double read FMinValue;
      property TransAction: TTRansID read FTransAction;
      property StopTrading: boolean read FStopTrading;
      property Memo: TMemo read FMemo;
      property CEntLabel: TLabel read FCEntLabel;
      property СExitLabel: TLabel read FСExitLabel;
      property FutLabel: TLabel read FFutLabel;
      property SpotLabel: TLabel read FSpotLabel;
      property MaxLabel: TLabel read FMaxLabel;
      property MinLabel: TLabel read FMinLabel;
      property CBLabel: TLabel read FCBLabel;
      property IntLabel: TLabel read FIntLabel;
      property ExtLabel: TLabel read FExtLabel;
      property DivLabel: TLabel read FDivLabel;
      property AvlLabel: TLabel read FAvlLabel;
      property MCntLabel: TLabel read FMCntLabel;
      property DeltaLabel: TLabel read FDeltaLabel;
      property CurDivLabel: TLabel read FCurDivLabel;
      property Settings: TExpertSettings read FSettings;
      property FutVol: double read FFutVol;
      property SpotVol: double read FSpotVol;
      property InData: TTransData read FInData;
      property OrderPrice: Double read FOrderPrice;
      property TrRes: Long read FTrRes;
      property ExCode: Long read FExCode;
      property TCode: Long read FTCode;
      property RepMess: LPSTR read FRepMess;
      property Volume: Quantity read FVolume;
      property aSell: Long read FaSell;
      property MustSpotVol: double read FMustSpotVol;
      property DllConn: boolean read FDllConn;
      property QConn: boolean read FQConn;
      property WrongData: TWrongData read FWrongData;
      property EntEdit: TEdit read FEntEdit;
      property ExittEdit: TEdit read FExitEdit;
  end;

 И в этом экземпляре Класса есть переменная (FInData: TTransData) - в которую принимаются данные, а дочерняя форма служит лишь для идентификации экземпляра класса и содержит несколько функций, через которые можно обращаться

к данному экземпляру класса. Что поток запускать (кстати тоже класс), что экземпляр Класса - одно и тоже.

 
Vitalii Ananev #:


Еще вопрос в процессе работы используете функцию  Application.ProcessMessages?

Нет, это робот, а не кинопроектор, отображение информации на дочерней форме это не главное, главное это вычисления и команды, которые выполняет экземпляр Класса TExpert

 
prostotrader #:

Спасибо, конечно, но Вы вероятно не поняли.

Дочернее окно лишь окно, в котором отображается информация, в всю работу выполняет класс

 И в этом экземпляре Класса есть переменная (FInData: TTransData) - в которую принимаются данные, а дочерняя форма служит лишь для идентификации экземпляра класса и содержит несколько функций, через которые можно обращаться

к данному экземпляру класса. Что поток запускать (кстати тоже класс), что экземпляр Класса - одно и тоже.

Не одно и тоже. Почитайте что такое многопоточные приложения. Вы говорите, что во время работы функции UpdExpertData() передача данных следующему окну не происходит. Что бы этого избежать надо что бы эта функция запускалась в отдельном потоке. И эти разные потоки будут работать одновременно (параллельно). Если вы запускаете просто экземпляр класса а затем еще какой то экземпляр класса в рамках одного потока. То второй класс как бы стоит в очереди пока не отработает 1-й запущенный класс. А если каждый класс запустить как два отдельных потока то они будут выполнятся одновременно и при этом еще будет работать главный поток. 

 

Все прекрасно работает, но функция OnData()

procedure TMDIChild.OnData;
var
  done: boolean;
begin
  FExpert.FinData:= TTransData(Data^); //resive data
  Mutex.Lock;
  try
    done:= Expert.TransBusy;  //Ckeck transaction in progress
  finally
    Mutex.Unlock;
  end;
  if(done = false) then FExpert.UpdExpertData(); //Uрdate expert data
end;

Исполняется достаточно долго, потому что приходится вызывать 

FExpert.UpdExpertData()

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

 
prostotrader #:

Нет, это робот, а не кинопроектор, отображение информации на дочерней форме это не главное, главное это вычисления и команды, которые выполняет экземпляр Класса TExpert

Application.ProcessMessages не только для отображения. PostMessage это постановка сообщения в конец очереди, SendMessage это отправка сообщения без очереди. ProcessMessages как бы заставляет принудительно выполнить сообщение находящееся в очереди.

 
prostotrader #:

Все прекрасно работает, но функция OnData()

Исполняется достаточно долго, потому что приходится вызывать 

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

Получается, что у вас поток данных не синхронизирован. PostMessage ставит в очередь оно асинхронное (как писал выше) если долго работает  UpdExpertData() нужно не посылать PostMessage пока не отработает UpdExpertData(). SendMessage после отправки сообщения дожидается отработки и ждет результата , Post же просто кладет сообщение в очередь. Выше немного не правильно написал. SendMessage тоже в очередь помещает сообщение.

 
Vitalii Ananev #:

Получается, что у вас поток данных не синхронизирован. PostMessage ставит в очередь оно асинхронное (как писал выше) если долго работает  UpdExpertData() нужно не посылать PostMessage пока не отработает UpdExpertData().

Так сейчас и реализовано,

Child.OnData(@OutData);  //Send data
PostMessage(Child.Expert.EHandle, WM_ON_TICK, Child.Expert.EHandle, 0);  //Init OnTick

но при этом притормаживается отдача данных в другой эксперт

А нужно просто передать данные, передал и вызвал PostMessage()

А PostMessage() вызовет OnTick() к классе, который и обработает данные.

У меня не получается "обрубить" передаваемый буфер.
 
prostotrader #:

Так сейчас и реализовано,

но при этом притормаживается отдача данных в другой эксперт

А нужно просто передать данные, передал и вызвал PostMessage()

Попробуйте смоделировать ситуацию. Вызвали Child.OnData(@OutData); он отработал дальше  PostMessage поставила в очередь сообщение №1. Оно в очереди и пока еще не отработано или в процессе работы. Потом опять Child.OnData(@OutData) и следом PostMessage №2 ставится в очередь. При этом сейчас обрабатывается сообщение №1. И так далее. Нужна синхронизация.

 
Vitalii Ananev #:

Попробуйте смоделировать ситуацию. Вызвали Child.OnData(@OutData); он отработал дальше  PostMessage поставила в очередь сообщение №1. Оно в очереди и пока еще не отработано ил в процессе работы. Потом опять Child.OnData(@OutData) и следом PostMessage №2 ставится в очередь. При этом сейчас обрабатывается сообщение №1. И так далее. Нужна синхронизация.

Вы не понимаете, что PostMessage() не ноиер 1 и номер 2 и т.д

Postmessage() посылается в конкретное окно, мало того в один из параметров заносится хендл окна

PostMessage(Child.Expert.EHandle, WM_ON_TICK, Child.Expert.EHandle, 0);  //Init OnTick
Child.Expert.EHandle

Это идентификатор окна в которое посылается сообщение.

BOOL PostMessageA(
  [in, optional] HWND   hWnd,
  [in]           UINT   Msg,
  [in]           WPARAM wParam,
  [in]           LPARAM lParam
);
[in, optional] hWnd

Type: HWND

A handle to the window whose window procedure is to receive the message. The following values have special meanings.