2017-02-17 51 views
5

Mam program, który używa wątku, który wykonuje trochę pracy. Wątek powinien powiadomić inny wątek (w tym przykładzie główny wątek) postępu.Obsługa zmiennej lokalnej w anonimowej procedurze przekazanej do TThread.Queue

Jeśli używam Synchronizuję(), aby wykonać synchronizację, wszystko działa zgodnie z oczekiwaniami. Gdybym synchronizacji z głównego wątku i publikują zmiennej dla i umieścić go na liście każda pojedyncza wartość get drukowane prawidłowo w moim ListBox:

procedure TWorkerThread.Execute; 
var 
    i: Integer; 
begin 
    inherited; 

    for i := 1 to 1000 do 
    begin 
    Synchronize(
     procedure() 
     begin 
     FireEvent(i); 
     end); 
    end; 
end; 

Wyjście: 1, 2, 3, 4, 5. .. 1000

Jeśli użyć kolejki() w celu przeprowadzenia synchronizacji wyjściowe nie są, jak oczekiwano,

procedure TWorkerThread.Execute; 
var 
    i: Integer; 
begin 
    inherited; 

    for i := 1 to 1000 do 
    begin 
    Queue(
     procedure() 
     begin 
     FireEvent(i); 
     end); 
    end; 
end; 

wyjściowa: 200, 339, 562, 934, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, [...]

Co się tutaj dzieje? Z mojego rozumienia anonimowa procedura powinna uchwycić zmienną "i"?

+0

PS: Wiem, że nie ma sensu aktualizować interfejsu użytkownika tak często. Chcę tylko wiedzieć, co zmienia zmienną zawartość, mimo że metoda anonimowa powinna przechwycić wartość. –

+1

Przechwytujesz zmienną. Ale próbujesz uchwycić "wartość". Musisz więc utworzyć nową zmienną, po jednej dla każdej iteracji pętli i przechwycić ją. To wymaga nowej ramki stosu, a zatem wywołania funkcji. Co prowadzi do kodu w odpowiedzi LURD. –

Odpowiedz

5

Anonimowa procedura przechwytuje odniesienie do zmiennej. Oznacza to, że wartość jest nieokreślona podczas wykonywania anonimowej procedury.

W celu uchwycenia wartości, trzeba zawinąć go w unikalnej ramie tak:

Type 
    TWorkerThread = class (TThread) 
    ... 
    function GetEventProc(ix : Integer): TThreadProcedure; 
    end; 

function TWorkerThread.GetEventProc(ix : Integer) : TThreadProcedure; 
// Each time this function is called, a new frame capturing ix 
// (and its current value) will be produced. 
begin 
    Result := procedure begin FireEvent(ix); end; 
end; 

procedure TWorkerThread.Execute; 
var 
    i: Integer; 
begin 
    inherited; 

    for i := 1 to 1000 do 
    begin 
    Queue(GetEventProc(i)); 
    end; 
end; 

Zobacz także Anonymous methods - variable capture versus value capture.

+0

To nie kompiluje ... –

+0

Powinien być 'Kolejka (EventWithValue (i))' i 'EventWithValue' powinien zwrócić' TThreadProcedure'. Przynajmniej tak bym to zrobił. –

+0

Przeczytaj dokumentację: [Anonymous Methods in Delphi] (http://docwiki.embarcadero.com/RADStudio/en/Anonymous_Methods_in_Delphi), w szczególności [Anonymous Methods Variable Binding] (http://docwiki.embarcadero.com/RADStudio/ pl/Anonymous_Methods_in_Delphi # Anonymous_Methods_Variable_Binding). –