Results 1 to 10 of 10

Thread: queue en anonymous methods.

  1. #1
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357

    queue en anonymous methods.

    Ik heb een thread die een queue verwerkt (iets camera gerelateerds), en die werkte nog met windows messages.

    Ter modernizatie wilde ik queue met een anonymous method gebruiken (om een parameter te geven), maar dat gaat finaal mis. In de oude code zat dus op de plek van de queue() een postmessage en dat werkte wel.

    Het probleem lijkt te zijn dat als er b.v. 3 items in ActiveQueue zitten (de lus loopt dus 3 keer, redelijk snel achter elkaar) dat backmsg.objs[0] overschreven wordt in de tweede of derde iteratie waarschijnlijk door syncobject() van de eerste iteratie die freeandnil op objs[0] aanroept.

    Het lijkt dus dat queue niet backmsg meeneemt, maar een pointer naar (de stack positie van?) backmsg.

    Herkent iemand dit probleem? Is er een makkelijke workaround? Ik gebruik normaal (threadlist) queues om extra allocaties te vermijden, maar dit zit in een niet erg snelheidskritisch deel, vandaar de experimenten.


    Delphi Code:
    1. while ActiveQueue.length>0 do
    2.     begin
    3.       x:=tcameramsg(activequeue.pop);
    4.       scan(x.msg);
    5.       newlist:=Tstringlist.Create;
    6.       newlist.Assign(Simulationlist);
    7.       backmsg:=simuclosurepool.getitem;
    8.       backmsg.msgid:=SMSGID_SIMULIJST;
    9.       backmsg.objs[0]:=newlist;
    10.       queue(procedure
    11.                 begin
    12.                   syncobject(backmsg);
    13.                 end
    14.                 );
    15.      end;
    Last edited by marcov; 18-Apr-19 at 12:46.

  2. #2
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Als ik het bereiden en versturen van de message (alles van de .getitem tot en met de queue()) naar een aparte method verplaats gaat het wel goed.

    Ik heb dus een vaag vermoeden waarom (hoe de compiler variabelen invangt voor anonymous methods), maar als hier (vuist)regels voor zijn, dan zou ik het graag horen.

  3. #3
    Volgens mij komt dat omdat je in feite maar een keer backmsg gedeclareerd hebt. Je sluit dus drie keer dezelfde variabele in, en als de waarde verandert, dan verandert die dus in alledrie de versies van de anonymous method. Volgens mij zou je dit bijvoorbeeld op kunnen vangen door een functie te maken die `backmsg` als parameter meekrijgt, en die de anonymous procedure teruggeeft. Als je dat een self invoking anonymous function maakt, dan ben je al bijna JavaScript aan het doen.
    1+1=b

  4. #4
    Dus, versie 1 doet botweg wat jij doet, volgens mij: Ik maak 3 procedures die allemaal i tonen. Het resultaat: 3 keer de message "3" (waarde van i na de lus):

    Delphi Code:
    1. procedure Versie1;
    2. const
    3.   MaxProcs = 3;
    4. var
    5.   procs: array[0..MaxProcs-1] of TProc;
    6.   proc: TProc;
    7.   i: Integer;
    8. begin
    9.  
    10.   i := 0;
    11.   while i < MaxProcs do
    12.   begin
    13.     procs[i] :=
    14.       procedure
    15.       begin
    16.         ShowMessage(i.ToString);
    17.       end;
    18.     Inc(i);
    19.   end;
    20.  
    21.   for proc in procs do
    22.     proc();
    23. end;
    Versie 2, met een direct aangeroepen anonieme wrapper functie, die zijn parameter, i, kopieert in de vorm van de parameter value, en een procedure teruggeeft die niet i, maar value toont. Het resultaat: messages met de waardes 0 t/m 2:
    Delphi Code:
    1. procedure Versie2;
    2. const
    3.   MaxProcs = 3;
    4. var
    5.   procs: array[0..MaxProcs-1] of TProc;
    6.   proc: TProc;
    7.   i: Integer;
    8. begin
    9.  
    10.   i := 0;
    11.   while i < MaxProcs do
    12.   begin
    13.     procs[i] := (function(value: Integer): TProc
    14.       begin
    15.         Result :=
    16.           procedure
    17.           begin
    18.             ShowMessage(value.ToString);
    19.           end;
    20.       end)(i);
    21.     Inc(i);
    22.   end;
    23.  
    24.   for proc in procs do
    25.     proc();
    26. end;

    Hier zit wel een aanname in. Dit werkt met integers, maar jij met objecten waarvoor het niet per se hetzelfde zou hoeven te zijn...
    1+1=b

  5. #5
    Nog een keer hetzelfde fenomeen, maar nu met een variabele van het type TComponent. Het effect lijkt hetzelfde, en het lijkt er dus inderdaad op dat de variabele ingesloten wordt, en niet zozeer de waarde van de pointer op het moment van insluiten. Eigenlijk wel logisch ook.

    In de post hierboven had ik een while loop gebruikt, om te vookomen dat er al te veel compiler magic met i zou gaan gebeuren. Hieronder gewoon een for loop gebruikt. Versie 1 geeft nu dus '2', '2', '2', en versie 2 geeft nog steeds '0', '1', '2'.

    Delphi Code:
    1. procedure Versie1;
    2. const
    3.   MaxProcs = 3;
    4. var
    5.   procs: array[0..MaxProcs-1] of TProc;
    6.   proc: TProc;
    7.   i: Integer;
    8.   o: TComponent;
    9. begin
    10.   for i := 0 to 2 do
    11.   begin
    12.     o := TComponent.Create(Application);
    13.     o.Tag := i;
    14.     procs[i] :=
    15.       procedure
    16.       begin
    17.         ShowMessage(o.Tag.ToString);
    18.       end;
    19.   end;
    20.  
    21.   for proc in procs do
    22.     proc();
    23. end;
    24.  
    25. procedure Versie2;
    26. const
    27.   MaxProcs = 3;
    28. var
    29.   procs: array[0..MaxProcs-1] of TProc;
    30.   proc: TProc;
    31.   i: Integer;
    32.   o: TComponent;
    33. begin
    34.   for i := 0 to 2 do
    35.   begin
    36.     o := TComponent.Create(Application);
    37.     o.Tag := i;
    38.     procs[i] := (function(o: TComponent): TProc
    39.       begin
    40.         Result :=
    41.           procedure
    42.           begin
    43.             ShowMessage(o.Tag.ToString);
    44.           end;
    45.       end)(o);
    46.   end;
    47.  
    48.   for proc in procs do
    49.     proc();
    50.  
    51. end;


    Self invoking functions pattern, zoals het gebruikt wordt in JavaScript. Daar is het heel gebruikelijk om op die manier de variabelen in de juiste context te vangen.
    1+1=b

  6. #6
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    De observatie van het eerste fragment zijn wel hetzelfde als wat ik heb. De locatie van de variable wordt gecaptured, niet de inhoud zelf.

    Dat met die parameter had ik ook uitgevonden, maar helaas komt dat niet overeen met de signature van tthread.queue()

    Die vereist een "reference to procedure" geloof ik.

  7. #7
    Klopt, de truc is om die functie direct aan te roepen. Die functie zelf hoeft ook niet anoniem te zijn, trouwens. Het gaat er om dat de proc die je (in mijn versie 2) in je queue stopt, niet de variabele uit de hoofdprocedure heeft gecaptured, maar de waarde uit de functie. En dat is, steeds een nieuwe variabele, omdat het steeds een nieuwe aanroep is.

    Die functie had ik natuurlijk ook anoniem gemaakt, om een beetje te zieken, -eh-, om de analogie met JavaScript te kunnen trekken, maar als je die functie los trekt, ziet het er zo uit, en zie je dat er twee verschillende TComponent variabelen zijn. Eentje in de hoofd procedure, en eentje als parameter van de functie. Waarschijnlijk kan je deze oplossing stuk maken door die parameter var of const te maken...

    Delphi Code:
    1. procedure Versie2b;
    2.  
    3.   function CreateQueueProc(o: TComponent): TProc;
    4.   begin
    5.     Result := procedure
    6.       begin
    7.         ShowMessage(o.Tag.ToString);
    8.       end;
    9.   end;
    10.  
    11. const
    12.   MaxProcs = 3;
    13. var
    14.   procs: array[0..MaxProcs-1] of TProc;
    15.   proc: TProc;
    16.   i: Integer;
    17.   o: TComponent;
    18. begin
    19.   for i := 0 to 2 do
    20.   begin
    21.     o := TComponent.Create(Application);
    22.     o.Tag := i;
    23.     procs[i] := CreateQueueProc(o);
    24.   end;
    25.  
    26.   for proc in procs do
    27.     proc();
    28. end;
    Last edited by GolezTrol; 18-Apr-19 at 14:34.
    1+1=b

  8. #8
    Even getest. Als je die parameter o een const maakt gaat het nog steeds goed. Als je hem var maakt, dan zegt de compiler: [dcc32 Error] Unit1.pas(54): E2555 Cannot capture symbol 'o'
    1+1=b

  9. #9
    mov rax,marcov; push rax marcov's Avatar
    Join Date
    Apr 2004
    Location
    Ehv, Nl
    Posts
    10,357
    Dat is inderdaad zo ongeveer wat ik nu denk. Maar ipv assignen aan procs[i] Queue() ik hem, en de procs array is dan een beetje de synchronize/queue queue (:-)).

    Ik blijf het een beetje ondeterministisch en eng vinden. Ik gebruik wel anonymous functions, maar tot nu zonder invangen van data. Deels omdat ik me er al eens eerder aan gestoten heb.

    Lang geleden (D7) gebruikte ik windows messages voor thread->main communicatie (en ook vice versa(*)), maar de logica is al bijna een decennium uit de formulieren verdwenen, en dat soort synchronizatie was een tikje lelijk. Met d2006 ben ik queue() gaan gebruiken, maar doorgaans met eigen queues en pools, zodat er nooit data gedestroyed/gecreate wordt. (het zit of in de queue, of ongebruikt in de pool). Dan hoef je alleen de procedure die de queue drained te queue()n

    In dit geval zijn het maar enkele integers en een stringlist, en daar weer pools en queues voor maken leek me overkill. Viel echter vies tegen :-)

    (*) van mainthread naar camera werkt analoog, maar ipv queue, gebruik ik daar een tevent. Heel veel camera apis hebben mogelijkheid om extra mutex objecten aan hun blocking .grab() call toe te voegen, dus kan je daar een mooi systeem zonder polling en met lage latency van maken.
    Last edited by marcov; 18-Apr-19 at 16:30. Reason: onlogische zin

  10. #10
    Ja, je trapt er makkelijk in. Aan de andere kant is dat ook maar wennen. Net zoiets als 'vroeger' een object/reference variabele A assignen aan B, of doorgeven aan een functie, en dan verbaasd zijn als je geen deep copy van je object krijgt.
    1+1=b

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •