2017-02-17 36 views
5

У меня есть программа, которая использует Thread, который выполняет некоторую работу. Нить должна уведомить другой поток (в этом примере основной поток) о прогрессе.Обработка локальной переменной в анонимной процедуре, переданной в TThread.Queue

Если я использую Синхронизировать() для выполнения синхронизации все работает должным образом. Если синхронизировать с основным потоком и опубликовать переменной для и поместить его в список каждый значение приобретают напечатанная правильно в моем ListBox:

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

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

Выход: 1, 2, 3, 4, 5. .. 1000

Если я использую Очередь() для выполнения синхронизации на выходе не ожидается, как:

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

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

Выход: 200, 339, 562, 934, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, 1001, [...]

Что здесь происходит? Насколько я понимаю, анонимная процедура должна фиксировать переменную «i»?

+0

PS: Я знаю, что это не имеет смысла обновлять интерфейс. Я просто хочу знать, что изменяет содержимое переменной, но метод anonymouse должен фиксировать значение. –

+1

Вы фиксируете переменную. Но вы пытаетесь захватить «ценность». Поэтому вам нужно создать новую переменную, по одной для каждой итерации цикла, и захватить ее. Для этого требуется новый стек стека и, следовательно, вызов функции. Что приводит к коду в ответе LURD. –

ответ

5

Анонимная процедура фиксирует ссылку на переменные. Это означает, что значение не определено, когда выполняется анонимная процедура.

Для того, чтобы захватить значение, вы должны обернуть его в уникальный кадр, как это:

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; 

Смотрите также Anonymous methods - variable capture versus value capture.

+0

Это не компилируется ... –

+0

Должно быть 'Queue (EventWithValue (i))' и 'EventWithValue' должно возвращать' TThreadProcedure'. По крайней мере, это то, что я буду делать. –

+0

Прочитайте документацию: [Анонимные методы в Delphi] (http://docwiki.embarcadero.com/RADStudio/en/Anonymous_Methods_in_Delphi), в частности [Анонимные переменные переменных переменных] (http://docwiki.embarcadero.com/RADStudio/ ан/Anonymous_Methods_in_Delphi # Anonymous_Methods_Variable_Binding). –