Предоставление следующего кода:
procedure TForm1.Button1Click(Sender: TObject);
var
myTask: ITask;
begin
myTask := TTask.Run(
procedure
var
i: Integer;
begin
for i := 0 to 3 do
begin
TThread.ForceQueue(
nil
, procedure
begin
ShowMessage('Count: ' + i.ToString);
end
);
//I know this will work
// TThread.Synchronize(
// nil
// , procedure
// begin
// ShowMessage('Count: ' + i.ToString);
// end
// );
end;
end
);
end;
Это всегда будет печатать «Count: 4»
Есть ли способ сохранить переменную i
при выполнении анонимной процедуры в очереди?
Я знаю, что это будет работать с использованием Synchronize()
, но я предпочитаю использовать Queue
, потому что в моем реальном случае метод очереди записывает некоторый журнал в файл. Моей задаче не нужно ждать лога, поэтому я предпочитаю использовать Queue()
.
Я использую Делфи 10.3.
Во-первых, вы должны использовать TThread.Queue()
, а не TThread.ForceQueue()
. Последний предназначен для использования только в основном потоке пользовательского интерфейса, а не в потоке задач.
Во-вторых, анонимный метод захватывает ссылки на переменные, а не значения. Прочтите документацию, особенно раздел «Механизм связывания переменных». Вы ставите в очередь 4 анонимных метода, которые совместно используют одну переменную, поэтому все их результаты имеют одно и то же значение.
Чтобы решить эту проблему, переместите захват внутрь другой процедуры, чтобы анонимные методы больше не использовали одну переменную. Например:
procedure TForm1.Button1Click(Sender: TObject);
var
myTask: ITask;
begin
myTask := TTask.Run(
procedure
var
i: Integer;
procedure DoQueue(Value: Integer);
begin
TThread.Queue(
nil
, procedure
begin
ShowMessage('Count: ' + Value.ToString);
end
);
end;
begin
for i := 0 to 3 do
begin
DoQueue(i);
end;
end
);
end;