На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

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


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> Использование TThread , Можно ли использовать TThread для создания и добавления объекта в список
    Товарищи, объясните, пожалуйста, можно ли использовать класс TThread для чтения данных объекта из файла (цель - уменьшение времени загрузки, поскольку размер 100-200 Мб) и добавления его в список (глобальная переменная TObjectList) и как.
      В методе Execute потока нужно выполнить код, занимающийся загрузкой.
      По окончани загрузки заблокировать TObjectList от изменения другими потоками (например, с помощью критической секции или даже Synchronize), добавить в него объект, и разлочить.

      Поток, занимающийся чтением, потратит столько же времени на загрузку, сколько и было, т.е. уменьшения времени загрузки не произойдет.
      Однако параллельно будет обслуживаться пользовательский интерфейс (программа не "зависнет" на время загрузки) и могут выполняться другие действия.
      Сообщение отредактировано: MBo -
        Спасибо, MBo, так и получилось:
        ExpandedWrap disabled
            TLoaderThread = class(TThread)
              private
               FMap: TMap;
               FTag: integer;
              protected
                procedure UpdateResults;
                procedure Execute; override;
              public
               constructor Create(Suspended: boolean; ATag: integer);
            end;


        и в коде:
        ExpandedWrap disabled
          constructor TLoaderThread.Create(Suspended: Boolean; ATag: Integer);
          begin
            inherited Create(Suspended);
            FTag:= ATag;
          end;
           
          procedure TLoaderThread.UpdateResults;
          begin
              BigMap.Add(FMap);
          end;
           
          procedure TLoaderThread.Execute;
          begin
              try
                 FMap:= LoadMap(FTag);
                 Synchronize(UpdateResults);
              except
                   on E: Exception do ShowMessage(E.ClassName+': '+E.Message);
              end;
          end;


        вызов:
        ...
        ExpandedWrap disabled
          LoaderThread:= TLoaderThread.Create(True, I);
                      LoaderThread.FreeOnTerminate:= true;
                      LoaderThread.Priority:= tpHighest;
                      LoaderThread.Resume;

        ...

        По ощущениям - немного медленнее прямой загрузки, зато процесс продолжается и программа не подвисает, только почему-то не закрашиваются :( полигоны загруженной карты, хотя все вроде выполняется. Может ли поток загрузки не успевать за основным потоком? Это я так думаю потому, что карта загружена, обрисовка объектов по контуру происходит все время и контуры есть, а закраска через тесселяцию (речь об OpenGL) и запекание в текстуру. При прямой загрузке есть все.
        Сообщение отредактировано: mnj -
          > только почему-то не закрашиваются полигоны загруженной карты
          Про это мы ничего пока не знаем
            Цитата mnj @
            Может ли поток загрузки не успевать за основным потоком?

            Может.
              Про полигоны:
              на старте при создании формы читаем заголовочную часть (список карт в файле) в MapList, определяем по нему нужную карту и грузим ее функцией LoadMap

              ExpandedWrap disabled
                function LoadMap(Tag: integer): TMap;
                var F: TStream;
                    I, J, K: integer;
                    B: byte;
                    MapObject:  TMapObject;
                    Pt: TMyPoint;
                begin
                   try
                    try
                     F:= TFileStream.Create('map.cmf', fmOpenRead, fmShareDenyNone);
                 .......................
                      Map:= TMap.Create;   //пустая карта, читаем габаритные границы
                           F.ReadBuffer(Map.PlaneLimits.SW.Lat, SizeOf(Map.PlaneLimits.SW.Lat));
                           F.ReadBuffer(Map.PlaneLimits.SW.Lon, SizeOf(Map.PlaneLimits.SW.Lon));
                               ...................
                               читаем разные данные карты и ее объекты
                               ..........................
                             F.ReadBuffer(MapObject.Size.SizeX, SizeOf(MapObject.Size.SizeX));  //ширина
                              ......................................
                 
                             if (MapObject.Local = LND) or (MapObject.Local = SQR) then //если объект является площадным, то
                             //--------------------------------------------------------------------------------------------
                             Tesselation(MapObject.Key,MapObject.CoordList);            //делаем мозаику из треугольников
                             //--------------------------------------------------------------------------------------------
                             Map.ListOfObjects.Add(MapObject);                          //и добавляем в список  
                             ..........................
                           Map.Loaded:= True;         //карта загружена
                           Result:= Map;
                    except    
                        ................
                   finally
                     FreeAndNil(F);
                     frmPrBar.Close;
                   end;
                end;

              В свою очередь
              ExpandedWrap disabled
                 //Тесселяция полигонов
                procedure Tesselation(MapObjectKey: integer; Points: TList);
                var TessObj: GLUTesselator;
                    I: integer;
                    A: ArrayOfVector;
                    P: TMyPoint;
                begin
                     TessObj := gluNewTess;
                     gluTessCallback(TessObj, GLU_TESS_VERTEX, @glVertex3dv);
                     gluTessCallback(TessObj, GLU_TESS_BEGIN, @glBegin);
                     gluTessCallback(TessObj, GLU_TESS_END, @glEnd);
                     gluTessCallback(TessObj, GLU_TESS_ERROR, @errorCallback);
                     gluTessCallback(TessObj, GLU_TESS_COMBINE, @combineCallback);
                     gluTessNormal(TessObj,0,0,1);
                     gluTessProperty(TessObj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD);
                    //
                     SetLength(A, Points.Count);
                     for I := 0 to Points.Count-1 do
                     begin
                         P:= Points.Items[I];
                         A[I,0]:= (P.Lon-StartLon)/Un;
                         A[I,1]:= (P.Lat-StartLat)/Un;
                         A[I,2]:= 0;
                     end;
                    glGenLists(1);
                     glNewList(MapObjectKey, GL_COMPILE);
                     glShadeModel(GL_FLAT);
                      gluTessBeginPolygon (TessObj, nil);
                       gluTessBeginContour(TessObj);   //начали контур
                        for I := 0 to Length(A)-1 do
                         gluTessVertex(TessObj, @A[I], @A[I]);
                       gluTessEndContour(TessObj);     //закончили контур
                      gluTessEndPolygon(TessObj);
                     glEndList;
                     gluDeleteTess(TessObj);
                end;

              Генерация списка происходит (смотрел в отладчике)

              ExpandedWrap disabled
                Далее отрисовка (вызывается в onpaint формы)
                procedure TGLForm.DrawMap;
                ..............
                    //собственно рисование
                  //натягиваем текстуру на прямоугольник
                     glEnable(GL_TEXTURE_2D);
                     CurrentPoint.Lon:= -cX;             //точка ВС
                     CurrentPoint.Lat:= -cY;
                     for I := 0 to 8 do
                     begin
                         glBindTexture(GL_TEXTURE_2D, TexId[I]);
                         MFrame:= Frames[I];     //квадрат для текстуры из массива
                .........................
                        then begin
                               RenderTexture(I);   //рендер в текстуру
                               MFrame.Rendered:= True;
                            end;
                            MFrame.Show(MFrame.Limits);
                         end
                 .................................
                    DrawMessage;
                end;

              Все так же выполняется, как и в прямой загрузке
              Рендер вызывает процедуру рисования каждого объекта (по списку)
              ExpandedWrap disabled
                procedure TGLForm.DrawObject(MapObject: TMapObject);
                var I, J: integer;
                    Stop: boolean;
                    Color: TVector;
                    Pt: TMyPoint;
                    aVertex: array of array of GLFloat;
                begin
                    I:= 0;
                    Stop:= False;
                    repeat
                      if MapObject.Class_code = FilterObjects[I].Code then
                      begin
                        if (MapObject.Local = SQR) or (MapObject.Local = LND) then
                        begin
                            glEnable(GL_BLEND);
                            glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) ;
                            Color:= ColorToRGB(FilterObjects[I].Color);
                            glColor4f(Color[0],Color[1],Color[2], FilterObjects[I].Transparency);
                            glCallList(MapObject.Key);


              И список тоже вызывается и значение ключа (номер списка) тоже уникальное. Все проверено в отладчике. При прямом вызове (при начальной загрузке) все работает, если вызывать прямо при необходимости - тоже работает, останавливая выполнение программы на время подгрузки, через thread - все команды проходят, даже до появления заметных лысин в надвигающейся карте, но...
              но результат выглядит как будто список glCallList не вызывается при загрузке через thread: скр1 сделан с закомментаренной командой glCallList, а скр2 с работающей (из-за большого размера я положил их к себе https://yadi.sk/d/5IyViG3T3FEzdm). Решения я пока не вижу .
                Быть может (у меня так было), что надо создавать их (gl-списки) в разных контекстах (wglCreateContext), а потом (ввиду разных потоков) рассказать, что это всё своё - wglShareLists(...).
                  Славян, можно поподробнее?
                    Ну схематично где-то так:
                    1.До запуска потоков надо насоздавать контекстов сколь и потоков. Я под винду фурычу, поэтому вызовами wglCreateContext.
                    2.В каждом потоке при запуске сказануть, что в своём контексте робим - wglMakeCurrent. Это всё оттого, что openGL не приспособлен был изначально для многопоточности. :'(
                    3.У меня тоже "до" сказано, что списки общие:
                    hRC2 = wglCreateContext( hDC );
                    wglShareLists( hRC, hRC2);
                      Спасибо, Славян, именно так я и сделал (в 2 часа ночи :wacko: ). Нашел на https://www.khronos.org/opengl/wiki/OpenGL_..._multithreading. Работает. И еще раз спасибо MBo
                        Поток - странная штука: если грузить в основном потоке, рисует правильно, если в параллельном - появляются какие-то необъяснимые артефакты, при том, что для загрузки используется одна и та же процедура. Может, кто подскажет, в какую сторону копать? (картинки лежат https://yadi.sk/d/8MG8y5XR3FZRZn)
                          Залезу в чужую тему чтобы не создавать новую похожую :)
                          Задача похожа на топикстартерную - есть основной поток и есть потоки, занимающиеся чтением из файлов данных, декодированием их и складыванием в буфер.
                          В чём проблема - класс потока я хотел бы использовать в нескольких похожих по функциям приложениях без какой-либо модификации. Поэтому хотелось бы, чтобы буфер данных создавался и обслуживался самим потоком. А основная программа просто получала бы нужные ей данные из потока.
                          В связи с этим у меня возникли вопросы:
                          1. Если в в TThread описываю Public процедуру или функцию и вызываю её из основного потока - она выполняется в основном потоке или в потоке обработке данных?
                          2. Если ответ на 1 - в основном потоке, то надо ли разделять процедуры на вызываемые "изнутри" потока обработки данных и снаружи? То есть возможен ли какой-то конфликт, если некую public процедуру я вызову из основного класса и во время её выполнения её же вызовет поток обработки данных.
                          3. Данные у меня лежат в TThreadList. Основной поток должен обращаться к потоку обработки данных и получать один объект из листа. Как с ним дальше работать? То есть я лочу лист, получаю доступ к списку объектов, получаю объект, разлочиваю лист. Но сам объект то не залочен. То есть как мне в основном потоке корректно обращаться к объекту, к которому может обратиться и поток обработки данных? Или мне просто копировать объект в локальные данные основного потока и только после этого разлочивать лист?
                            >Если в в TThread описываю Public процедуру или функцию и вызываю её из основного потока - она выполняется в основном потоке

                            В основном - выполняется в контексте вызвавшего потока

                            >То есть возможен ли какой-то конфликт, если некую public процедуру я вызову из основного класса и во время её выполнения её же вызовет поток обработки данных.

                            Конфликта кода не будет, но если процедура использует общие данные, два её экземпляра в разных потоках будут конфликтовать по данным

                            Т.к.
                            > к которому может обратиться и поток обработки данных
                            то проще всего копировать.

                            Но эта схема странноватая. Объект будет изменен и его снимок состояния станет неактуальным. Или так и задумано?
                            Сообщение отредактировано: MBo -
                              Цитата MBo @
                              В основном - выполняется в контексте вызвавшего потока

                              Ага, уже хорошо. То есть все сервисные процедуры можно описать в самом классе потока.

                              Попробую объяснить саму задачу.
                              У меня написан для рабочих целей набор видеоплейеров, которые воспроизводят видеоклипы на видеокарту BlackMagic decklink. От простейшего, который просто крутит клип по кругу, до довольно сложного с плейлистом и выводом на несколько видеокарт параллельно. Воспроизведение идёт бесподрывно, то есть клипы воспроизводятся друг за другом без подрыва в момент перехода, для этого используется библиотека gmfbridge. Всё это работает на DirectShow.
                              К сожалению, после довольно долгого использования у библиотеки обнаружилось немалое количество глюков. Последняя версия библиотеки выпущена лет 10 назад, автор её давно забросил. Разбираться в чужом коде мне совершенно не интересно.

                              Возникла идея переделать всё по принципу фрейм-серверов без использования directshow вообще. То есть каждый видеофайл открывается в своём экземпляре класса с помощью библиотек ffmpeg и класс выдаёт считанные кадры головному процессу, который пересылает их в нужном порядке видеокарте.
                              Соответственно, задача простая - передать в класс имя файла и параметры нужного выходного кадра. Класс создаёт буфер из N кадров, открывает файл и настраивает конвертер. Далее нужно реализовать следующее - головной процесс обращается к классу "дай мне кадр с таким-то таймстемпом". Класс просматривает свой буфер, находит нужный кадр и отдаёт ссылку на него головному процессу. Или говорит - у меня нет такого таймстемпа - подожди. После чего буфер полностью очищается, класс прыгает по файлу на нужную точку и заполняет буфер кадрами, начиная с запрошенного. После чего шлёт сообщение в головной поток - я готов.
                              Класс фреймсервера должен быть отдельным потоком, так как библиотека ffmpeg полностью синхронная и открытие файла может спокойно занимать несколько секунд (пока считаются все заголовки и индексы). А в это время основной процесс должен продолжать нормально работать.
                              Вот эта вот процедура запроса кадра с заданным таймстемпом - единственная, которую нужно вызывать из основного потока. Про неё и был вопрос.
                              И вопрос про вот этот вот "отданный" в основную процедуру кадр. Если мы отдаём непосредственно кадр из буфера потока, то мне нужно будет пометить этот кадр, как принадлежащий основному потоку, чтобы фреймсервер не пытался его повторно использовать. Достаточно ли для этого сделать boolean поле locked, например. И при отдаче кадра основному потоку ставить его в true, а когда он больше не нужен основному потоку - ставить его в false?
                              Иди лучше всё-таки при запросе кадра копировать его в новый буфер и отдавать этот буфер основной программе, а программа уже сама его очистит когда не надо? Тут я просто боюсь проблемы фрагментации памяти - каждый кадр будет примерно по 4 МБ и 25 раз в секунду просить от системы по 4МБ и тут же их отдавать - не будет ли проблем.
                                Цитата An_private @
                                1. Если в в TThread описываю Public процедуру или функцию и вызываю её из основного потока - она выполняется в основном потоке или в потоке обработке данных?

                                В том из которого вызвали. Без каких либо исключений.

                                Цитата An_private @
                                2. Если ответ на 1 - в основном потоке, то надо ли разделять процедуры на вызываемые "изнутри" потока обработки данных и снаружи? То есть возможен ли какой-то конфликт, если некую public процедуру я вызову из основного класса и во время её выполнения её же вызовет поток обработки данных.

                                Где есть общие данные, то там неприменимо возникает конфликт. Да, нужна синхронизация. А ещё лучше модель взаимодействия потоков.


                                Цитата An_private @
                                Данные у меня лежат в TThreadList.

                                Если у каждого потока будет своя корпия данных, то никаких конфликтов соответственно не будет. Но я бы данные удалил из списка. И только после этого снял Lock.


                                Цитата An_private @
                                Ага, уже хорошо. То есть все сервисные процедуры можно описать в самом классе потока.

                                Ну можно, только это решение через одно место. Вам придётся в каждом публичном методе сделать синхронизацию, а именно вставить критическую секцию.
                                А там не далеко до проблем известных как гонки потоков и DeadLock.

                                Цитата An_private @
                                так как библиотека ffmpeg полностью синхронная

                                А она потока безопасна?

                                Цитата An_private @
                                Достаточно ли для этого сделать boolean поле locked, например. И при отдаче кадра основному потоку ставить его в true, а когда он больше не нужен основному потоку - ставить его в false?

                                Не совсем. Не просто Boolean, а мьютекс.


                                Цитата An_private @
                                Иди лучше всё-таки при запросе кадра копировать его в новый буфер и отдавать этот буфер основной программе, а программа уже сама его очистит когда не надо? Тут я просто боюсь проблемы фрагментации памяти - каждый кадр будет примерно по 4 МБ и 25 раз в секунду просить от системы по 4МБ и тут же их отдавать - не будет ли проблем.

                                Эта неважно с вероятностью 90% вам в любом случае придётся писать свой менеджер памяти.

                                Лучше просто раскидать не по потокам, а по процессам.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0524 ]   [ 17 queries used ]   [ Generated: 19.04.24, 20:29 GMT ]