Совместное использование нативной переменной между потоками Delphi
Я предполагал, что если общая переменная между потоками имеет собственный тип, атомарность должна делать эту работу.
Но в соответствии с выводом кода ниже, это не так, по крайней мере, для Delphi.
Поток t1 просто увеличивает счетчик в 10 раз. В то же время поток t2 уменьшает счетчик в 10 миллионов раз. Таким образом, ожидаемое значение счетчика в конце равно 0, но я каждый раз читаю разные значения.
Как правильно делить нативную переменную между потоками в Delphi без блокировки?
procedure TForm1.Button1Click(Sender: TObject);
var
t1, t2: TThread;
Counter: NativeInt;
begin
Counter := 0;
// first thread to increment shared counter
t1 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Inc(Counter);
end
);
// second thread to decrement shared counter
t2 := TThread.CreateAnonymousThread(
procedure ()
var
i: Integer;
begin
for i := 1 to 10000000 do
Dec(Counter);
end
);
t1.FreeOnTerminate := false;
t2.FreeOnTerminate := false;
// start threads
t1.Start;
t2.Start;
// wait for them to finish
t1.WaitFor;
t2.WaitFor;
t1.Free;
t2.Free;
// print the counter, expected counter is 0
Caption := IntToStr(Counter);
end;
1 ответ
Чтение и запись выровненных переменных является атомарным. Но проблема в том, что когда вы используете inc
а также dec
Вы оба читаете и пишете. Выполняя два обращения к памяти, составная операция перестает быть атомарной.
Вместо этого используйте атомарные функции приращения. TInterlocked
методы класса или AtomicIncrement
,
Что касается того, что является родным о NativeInt
, что относится к его размеру. Это целочисленный тип того же размера, что и указатель. Итак, 32 бита в 32-битном процессе, 64 бита в 64-битном процессе. Эти типы редко используются для чистого кода Delphi, обычно для взаимодействия со сторонними библиотеками, которые могут объявлять типы дескрипторов, используя целочисленные значения указателя.