Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Delphi Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
 Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
Мужики, хотел внести для себя ясность в вопросе, которым только начал заниматься.

В приложении на главной форме FrmMain лежит TIBDatabase. Делаю коннект потоке (схематично)
procedure TMyThread.Execute;
begin
  try
    FrmMain.IBDatabase.Connected:= True;
  except
    //bla-bla-bla
  end;
end;  


Соответственным образом коннекчусь к таблям.
Под виндой проблем нет, под никсами приложение валится через раз.

Поэтому хотел уточнить, это корректный код? Если создавать компоненты доступа динамически (объявив, как приватные поля класса TMyThread, типа FIBDatabase), то после уничтожения этого экземпляра потока FIBDatabase тоже умрет?
=================
Док.

Win7 Ultim x64/Deb 8.7 i386:
FB 3.0.2.32703, диалект 3, SS(win)/SC(Deb),
Lazarus 1.9(r.54844); FPC 3.1.1 (r.36160), IBX by -Rik-; IBE 2016.5.14.1
22 май 17, 08:55    [20500242]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
В потоке только подключение?
Как потом с базой идет работа? Как форма узнает о результате?

Док
после уничтожения этого экземпляра потока FIBDatabase тоже умрет?

Вряд-ли, т.к. поток не компонент. Его не указать владельцем (чтоб он всех своих за собой "забрал").
22 май 17, 09:03    [20500263]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman
В потоке только подключение?

нет, там работа с таблями: селекты, апдейты, выполнение процедур на сервере
wadman
Как форма узнает о результате?

о каком результате речь?
22 май 17, 09:31    [20500311]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
о каком результате речь?

О результате работы потока. База-то на форме?

П.С. Если работа вся в потоке, то я-бы все необходимое для этой работы в потоке и создавал/удалял.
22 май 17, 09:34    [20500316]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman
О результате работы потока. База-то на форме?

Ну, естессно. В стартовом посте написал. После коннекта и пары проверок все селекты в гридах отображаются корректно. Вот только на Дебе падает через раз.

Откатиться, что ли, на предыдущие релизы Лазаря? Или попробовать UIB вместо IBX - ума не приложу...
22 май 17, 09:42    [20500337]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
После коннекта и пары проверок все селекты в гридах отображаются корректно.

Это же соединение потом в визуальной части используется? Если да, то как результат работы потока обрабатывается?
Из стартового поста не видно как реально используется поток и база.
Док
Или попробовать UIB вместо IBX - ума не приложу...

Вряд-ли это поможет.
Док
Вот только на Дебе падает через раз.

С какой ошибкой?
22 май 17, 09:48    [20500359]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman
Это же соединение потом в визуальной части используется? Если да, то как результат работы потока обрабатывается?

В потоке поэтапно проходит сначала коннект к базе, затем проверки (грузятся настройки юзверя для отображения ), затем открывается табля с гридом на форме и поток завешается и умирает

wadman
С какой ошибкой?

доберусь до дома, покажу
22 май 17, 10:45    [20500582]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10070
1. С одним коннектом может работать только один поток
2. Если несколько потоков создают коннекты, то эта процедура должна быть синхронизирована единым мьютексом
3. Пункт 2 распространяется на весь процесс. И если коннекты создаются в разных dll, то также должен использоваться единый мьютекс
22 май 17, 11:39    [20500806]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
_Vasilisk_, wadman
там код элементарный (на комменты не обращайте внимание, потом потру лишнее)

+ сам коннект
    { TDataMsg }

    TDataMsg = record
      Msg: DWord;
      WParam: Word;
      LParam: NativeInt;
    end;

    //вариант сценария работы дополнительного потока
    MyThreadTarget = (mttConnectDB, mttDisconnectDB, mttOpenDSet);   

//==========================
procedure TMyThread.Execute;
var MyMsg: TDataMsg;
    ErrMsg: String;
    i: Integer;
begin
  //реализуем тот или иной функционал потока в зависимости от заданной цели
  case FThreadTarget of
 {======================================================================================}
    //коннект к базе и основным таблицам
    mttConnectDB:
      try {цикл коннекта к базе и проверок}
        //задаем параметры коннекта
        with FrmMain.DBase_main do
          begin
            DatabaseName:= FDbName;
            LibraryName:= FLibName;
            Params.Add('lc_ctype=' + FChrSet);//чарсет коннекта
            Params.Add('user_name=' + FUsrNm);//имя юзера
            Params.Add('password=' +FPW);//пароль
            if Trim(FUsrRole) <> ''
              then Params.Add('sql_role_name=' + FUsrRole);//роль, если задана
          end;

        //запускаем фоновый поток коннекта
        try {коннект к базе данных}
          //коннектимся к базе (передаем сообщение в LabelMsg)
          MyMsg.LParam:= NewString('Коннектимся к базе данных');
          SendMessage(FrmMain.Handle, WM_GETFORSPLASHSTR_MSG, 0, MyMsg.LParam);

          //спрячем левую кнопку на сплэше
          MyMsg.WParam:= 0;
          MyMsg.LParam:= NewString('');
          SendMessage(FrmMain.Handle, WM_GETFORBTNLEFT_MSG, MyMsg.WParam, MyMsg.LParam);

          //текст для правой кнопки
          MyMsg.LParam:= NewString('Прервать');
          MyMsg.WParam:= 1;
          SendMessage(FrmMain.Handle, WM_GETFORBTNRIGHT_MSG, MyMsg.WParam, MyMsg.LParam);

          Sleep(1000);

          if not FrmMain.DBase_main.Connected then FrmMain.DBase_main.Connected:= True;

        except {коннект к базе данных}
           on E:EIBError do
             begin
               case EIBError(E).IBErrorCode of
         335544344: ErrMsg:= 'Не могу подключиться к указанному файлу базы данных. ' +
                             'Возможно, его уже успели переименовать, переместить, удалить или у вас нет на него прав';
         335544472: ErrMsg:= 'Введенный пароль не соответствует вашему логину. Уточните ваш логин и ' +
                             'пароль у администратора базы данных';
                 else
                    ErrMsg:= {$IFDEF UNIX} E.Message; {$ELSE} WinCPToUTF8(E.Message); {$ENDIF}
               end;


               //передаем текст сообщения в мемо
               MyMsg.lParam:= NewString(ErrMsg);
               SendMessage(FrmMain.Handle, WM_GETFORSPLASHSTR_MSG, 0, MyMsg.LParam);

               i:= 10;//счетчик на 10 циклов

               //немного заморозим отрисовку на(i) циклов, чтобы юзер смог осмыслить сообщение об ошибке,
               //пока не окончился таймер или не прилетело сообщение из сплеша, что там нажата кнопка 'Esc'
               while (i > 0) and (not FrmMain.FIsFinishedThread) do
               begin
                 Dec(i);
                 {$IFDEF UNIX}
                   MyMsg.LParam:= NewString(Format('Закрыть (%d)',[i]));
                   MyMsg.WParam:= 1; //нажатие левой кнопки с обратным отсчетом завершит поток
                   SendMessage(FrmMain.Handle, WM_GETFORBTNRIGHT_MSG, MyMsg.WParam, MyMsg.LParam);

                   MyMsg.LParam:= NewString('Сохранить в буфер обмена');
                   MyMsg.WParam:= 2; //нажатие правой кнопки сохранит текст ошибки в буфер обмена и завершит поток
                   SendMessage(FrmMain.Handle, WM_GETFORBTNLEFT_MSG, MyMsg.WParam, MyMsg.LParam);
                 {$ELSE}
                   MyMsg.LParam:= NewString(Format('Закрыть (%d)',[i]));
                   MyMsg.WParam:= 1; //нажатие левой кнопки с обратным отсчетом завершит поток
                   SendMessage(FrmMain.Handle, WM_GETFORBTNLEFT_MSG, MyMsg.WParam, MyMsg.LParam);

                   MyMsg.LParam:= NewString('Сохранить в буфер обмена');
                   MyMsg.WParam:= 2; //нажатие правой кнопки сохранит текст ошибки в буфер обмена и завершит поток
                   SendMessage(FrmMain.Handle, WM_GETFORBTNRIGHT_MSG, MyMsg.WParam, MyMsg.LParam);
                 {$ENDIF}

                 Sleep(1000);
               end;

               FrmMain.FIsFinishedThread:= True;//сообщим сплешу, что поток закончился и можно закрыться
               Exit;//переходим к секции finally
             end; {on E:EIBError do}
        end; {коннект к базе данных}

        try {коннект к таблице с настроками пользователя}
          with FrmMain do
          begin {with FrmMain do}
            MyMsg.LParam:= NewString('Загружаем или создаем настройки пользователя');
            SendMessage(Handle, WM_GETFORSPLASHSTR_MSG, 0, MyMsg.lParam);

            Sleep(1000);

            //проверяем, есть ли у этого юзера запись в таблице настроек
            QrySettings.SQL.Text:=
            'SELECT COUNT (RDB$DB_KEY) AS cnt FROM RDB$DOCSETTINGS WHERE NAME = UPPER(:prmUsrName)';
            QrySettings.Prepare;
            QrySettings.ParamByName('prmUsrName').Value:= FUsrNm;
            QrySettings.Open;

            //если нет, то создаем запись с его именем и дефолтовыми настройками
             if QrySettings.FieldByName('cnt').AsInteger = 0 then
             begin
               TransWrite_main.StartTransaction;
               try {try .. except попытка записи в таблю настроек}
                 ExecSql_Settings.SQL.Text:= 'EXECUTE PROCEDURE CREATE_DOCSETTINGS_NEW_USER (:prmNAME,:prmNOTE)';
                 ExecSql_Settings.Params.ByName('prmNAME').Value:= LowerCase(UsrNm);
                 ExecSql_Settings.Params.ByName('prmNOTE').Value:= '';
                 ExecSql_Settings.ExecQuery;
                 TransWrite_main.Commit;
               except {try .. except попытка записи в таблю настроек}
                 TransWrite_main.Rollback;
                 FCurrentUser:= 'DEFAULT_USER';//текущий юзер для загрузки настроек

                 // далее поскипал однотипный код
				 
                 //если нажали кнопку 'Нет' на сплэше, то переходим в блок finally и прерываем коннект
                 if FIsFinishedThread then Exit;
               end; {try .. except попытка записи в таблю настроек}
             end;
          end; {with FrmMain do}
        except {коннект к таблице с настроками пользователя}
          FrmMain.FCurrentUser:= 'DEFAULT_USER'; //текущий юзер для загрузки настроек
          // далее поскипал
          
          //если нажали кнопку 'Прервать' на сплэше, то переходим в блок finally и прерываем коннект
          if FrmMain.FIsFinishedThread then Exit;
        end; {коннект к таблице с настроками пользователя}


      {TODO: отладить настройки юзера}

        //грузим настройки юзера
        //FrmMain.LoadAppSettings;


        try {коннект к остальным данным}

          //коннектимся к таблице
          MyMsg.LParam:= NewString('Получаем данные');
          SendMessage(FrmMain.Handle, WM_GETFORSPLASHSTR_MSG, 0, MyMsg.LParam);

          //спрячем левую кнопку на сплэше, передав туда "нулевой" признак видимости
          MyMsg.WParam:= 0;
          MyMsg.LParam:= NewString('');
          SendMessage(FrmMain.Handle, WM_GETFORBTNLEFT_MSG, MyMsg.WParam, MyMsg.LParam);

          //текст для правой кнопки
          MyMsg.LParam:= NewString('Прервать');
          MyMsg.WParam:= 1;
          SendMessage (FrmMain.Handle, WM_GETFORBTNRIGHT_MSG, MyMsg.WParam, MyMsg.LParam);

          Sleep(1000);

          FrmMain.QrySelect.Active:= True;
        except {коннект к остальным данным}
         on E:EIBError do
           begin
             //текст ошибки
             {$IFDEF UNIX} ErrMsg:= E.Message; {$ELSE} ErrMsg:= WinCPToUTF8(E.Message); {$ENDIF}

             // далее поскипал однотипный код
             FrmMain.FIsFinishedThread:= True;//сообщим сплешу, что поток закончился и можно закрыться
             Exit;//переходим к блоку код в finally
           end;
        end; {коннект к остальным данным}


      finally {цикл коннекта к базе и проверок}
        //если ранее взвели флаг окончания потока
        if FrmMain.FIsFinishedThread
          then //то прервем коннект
            begin
              FrmMain.DBase_main.Connected:= False;
            end
          else //иначе доведем дело до конца
            begin
              //если коннект успешный, применяем загруженные настройки
              if FrmMain.DBase_main.Connected then FrmMain.SetAppSettings;

              //взведем флажок (чтобы сплэш знал о завершении потока и дал себя закрыть)
              FrmMain.FIsFinishedThread:= True;
            end;

        //закроем сплэш
        SendMessage(FrmMain.Handle,WM_CLOSESPLASHFORM_MSG,0,0);
      end; {цикл коннекта к базе и проверок} {mttConnectDB:}

{======================================================================================}
    mttDisconnectDB: ;

{======================================================================================}
    mttOpenDSet: ;

  end; {case FThreadTarget of}


end;    
+ создаю и запускаю поток
procedure TFrmMain.ActOpenDBExecute(Sender: TObject);
begin
  if Assigned(FConnectThread) then Exit;//если поток все еще крутится, выйдем отседова

  FCurrentUser:= UsrNm;//запоминаем залогинившегося юзера

  //создаем приостановленный поток
  FConnectThread:= TMyThread.Create(True);

  try
    //определяем, какая часть потока бцдет реализована
    FConnectThread.FThreadTarget:= mttConnectDB;

    //передаем параметры коннекта в фоновый поток
    with FConnectThread do
      begin
        FChrSet:= ChrSet;
        FDbName:= DBName;
        FLibName:= LibName;
        FUsrNm:= UsrNm;
        FPW:= PW;
        FUsrRole:= UsrRole;
      end;

    //создаем сплэш
    SplashForm:= TFrmSplash.Create(Application);

    //стартуем ожидающий поток
    FConnectThread.Start;
    try
      SplashForm.ShowModal;//показываем сплэш
    finally
      FreeAndNil(SplashForm);
    end;
  finally
    FreeAndNil(FConnectThread);
  end; 


В результате, на никсах, видно подтормаживание анимации, и через раз лезет окно с ошибкой (обычно в конце цикла коннекта)
+
Проект <тут название моего проекта> вызвал класс исключения 'External: SIGABRT'.

 По адресу B7FDCD40

В окне ассемблера
B7FDCD40 5d                       pop    %ebp

при попытке войти отладчиком в процедуру получаю
00000000 ??????

В файле проекта вроде все должно быть корректно
+
program andrology_pr;

{$mode objfpc}
{$H+}
{$codepage UTF8}

uses
  {$IFDEF UNIX}
  cthreads
  , cmem
  ,
  {$ENDIF}
  Interfaces, // this includes the LCL widgetset
  Forms, rxnew, ibexpress, main_u, main_functions_u, splash_u, variable_u,
  dm_pict_u;

{$R *.res}

begin
  Application.Scaled:=True;
  Application.Title:='<название проекта>';
  RequireDerivedFormResource := True;
  Application.Initialize;
  Application.CreateForm(TDM_pictures, DM_pictures);
  Application.CreateForm(TFrmMain, FrmMain);
  Application.Run;
end.


ps. У меня ощущение, что приложению оперативки не хватает (Деб крутится на виртуалке с RAM 2048 kB). пробовал выделить ей 4 гига, картина та же
22 май 17, 12:15    [20500950]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
В окне ассемблера
B7FDCD40 5d                       pop    %ebp

А выше что? Похоже, что перед выходом из некой процедуры стэк ломаный, возможно обращение к (уже) несуществующему объекту.
Док
if FrmMain.DBase_main.Connected then FrmMain.SetAppSettings;

Тут нет обращения к визуальной части?
22 май 17, 12:49    [20501068]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
makhaon
Member

Откуда: A galaxy far far away
Сообщений: 2297
1. в отладке может что-то видно?
2. в основном потоке код работает?
22 май 17, 13:15    [20501140]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
makhaon,

отладчик в Лазаре никакущий, в потоке вообще отладить что-либо сложно, если только пользовать DebugLn.

Как назло, решил переставить новую ревизию (в предыдущей че-та с настройками случилось - не сохраняются). Теперь не компилится с ошибкой "...lazarus_trunk\ide\sourcefilemanager.pas(6459,27) Error: (5038) identifier idents no member "DesignPPI"". Ужос!!! Картинка с другого сайта.

зы. щас, разберусь со средой, отвечу :)
22 май 17, 13:26    [20501175]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman
Тут нет обращения к визуальной части?

Есть, и еще какое.
+ примерно так
procedure TFrmMain.SetAppSettings;
begin
//определяем доступность грида в зависимости от состояния коннекта
Grid_main.Enabled:= DBase_main.Connected;

//настройки сетки
with Grid_main do
  begin
    if Enabled
      then
        begin
          TitleFont.Color:= clDefault;
        end
      else
        begin
          TitleFont.Color:= clGrayText;
        end;
  end;


//и далее в том же духе

// для отладки
if FrmMain.DBase_main.Connected
  then FrmMain.Label4.Caption:= 'connected'
  else FrmMain.Label4.Caption:= 'not connected';
end;
22 май 17, 14:02    [20501297]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
Есть, и еще какое.

Дак нельзя так делать.
22 май 17, 14:16    [20501340]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman
Дак нельзя так делать.

а если загрузка настроек из табли длительная, то как быть. В следующем потоке делать?
22 май 17, 15:31    [20501602]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
wadman
Дак нельзя так делать.

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

Суть не в этом, а в том, что сообщения (строками) для формы кидаешь-же сообщениями, что верно.
Так и это (применение настроек) нужно делать подобным образом.

Яж потому и спрашивал, как форма узнает, что поток отработал?
Тут же получается, что поток работает с базой параллельно с формой...
22 май 17, 15:41    [20501631]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
_Vasilisk_
Member

Откуда: Украина, Харьков
Сообщений: 10070
Док
а если загрузка настроек из табли длительная, то как быть.
Загрузить в буфер, отдать буфер основному потоку и там применить
22 май 17, 15:42    [20501635]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
_Vasilisk_
Загрузить в буфер, отдать буфер основному потоку и там применить

а как это технически выглядит, можно хотя бы схематично?
wadman
Тут же получается, что поток работает с базой параллельно с формой...

хм.... а я и не подумал.

Т.е., мне теперь в потоке каждый параметр вычитывать из табли и перегонять по одному сообщению в основную форму что ли? А нельзя их как-то массивом передать?
22 май 17, 15:51    [20501675]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
А нельзя их как-то массивом передать?

Хоть через TStringList (это один вариантов "буфера", что предложил _Vasilisk_).
22 май 17, 15:53    [20501691]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
закомментил
if FrmMain.DBase_main.Connected then FrmMain.SetAppSettings;

вроде валиться перестала. Щас буду думать, как параметры передавать: столбиком или в строчку :)
22 май 17, 16:25    [20501811]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
Док
Member

Откуда: Казань
Сообщений: 5395
wadman,

кстати, а какова максимальная длина строки, которую можно через NewString передать?
22 май 17, 16:26    [20501813]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
Док
кстати, а какова максимальная длина строки, которую можно через NewString передать?

С моей стороны нет ограничений, все на уровне ОС/языка по свободной памяти и строкам.
http://wiki.freepascal.org/Character_and_string_types#AnsiString тут и ниже.
22 май 17, 16:36    [20501841]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
makhaon
Member

Откуда: A galaxy far far away
Сообщений: 2297
Док,

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

автор
Вряд-ли, т.к. поток не компонент. Его не указать владельцем (чтоб он всех своих за собой "забрал").


можно компонентную обёртку всунуть вместо чистого потока.
22 май 17, 17:03    [20501976]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
YuRock
Member

Откуда: Донецк
Сообщений: 2774
_Vasilisk_
2. Если несколько потоков создают коннекты, то эта процедура должна быть синхронизирована единым мьютексом
3. Пункт 2 распространяется на весь процесс. И если коннекты создаются в разных dll, то также должен использоваться единый мьютекс

Если я не ошибаюсь, то такая особенность (необходимость синхронизации вызова isc_attach_database, т.е. TIBDatabase.Connected := True) была (что касается Firebird) до версии 3.0. А сейчас уже исправили (в fbclient.dll/so) это дело.
22 май 17, 17:10    [20502010]     Ответить | Цитировать Сообщить модератору
 Re: Lazarus: коннект в потоке  [new]
wadman
Member

Откуда: Санкт-Петербург
Сообщений: 23452
makhaon
можно компонентную обёртку всунуть вместо чистого потока.

Уже предлагал много раз. :( ладно хоть не на асме клепает.
22 май 17, 17:15    [20502030]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2 3   вперед  Ctrl      все
Все форумы / Delphi Ответить