AtomicCmpExchange()是否已损坏



如果您创建了一个新的多设备应用程序项目,请设置Project > Option > Compiling > Optimization : True,然后将下面的代码复制到unit1.pas:

unit Unit1;
interface
uses
System.SysUtils,
FMX.Forms,
FMX.StdCtrls,
System.Classes,
FMX.Types,
FMX.Controls,
FMX.Controls.Presentation;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
FKey: integer;
public
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.Button1Click(Sender: TObject);
begin
FKey := 2;
var LCompareKey: integer := 2;
AtomicCmpExchange(FKey{target}, LCompareKey{NewValue}, LCompareKey{Comparand});
if FKey <> LCompareKey then raise Exception.Create('Error 2');
TThread.queue(nil,
procedure
begin
if LCompareKey <> FKey
then raise Exception.Create('Error 3');
end);
end;
end.

为什么此代码在if FKey <> LCompareKey then raise Exception.Create('Error 2');上的Win32上崩溃?

我正在使用Delphi10.4 Sydney Update 3。我还没有尝试德尔福11亚历山大,所以我不知道它是否在那个版本中工作。

除了停用优化之外,还有其他解决方法吗?

另一个问题-激活优化真的安全吗?

是的,当优化打开时,Win32编译器上AtomicCmpExchange的代码生成已损坏。

问题与TThread.Queue调用中发生的匿名方法变量捕获结合在一起。在没有变量捕获的情况下,可以正确地生成AtomicCmpExchange的汇编代码。

解决此问题的方法是使用TInterlocked.CompareExchange

...
var LCompareKey: integer := 2;
TInterlocked.CompareExchange(FKey{target}, LCompareKey{NewValue}, LCompareKey{Comparand});
if FKey <> LCompareKey then raise Exception.Create('Error 2');
...

TInterlocked.CompareExchange函数仍然使用AtomicCmpExchange,但在调用位置,它通过参数而不是直接处理捕获的变量,并且生成的代码在这些情况下是正确的。

class function TInterlocked.CompareExchange(var Target: Integer; Value, Comparand: Integer): Integer;
begin
Result := AtomicCmpExchange(Target, Value, Comparand);
end;

另一个不太理想的解决方案是用{$O-}编译器指令关闭坏方法Button1Click的优化,然后用{$O+}重新打开它

由于AtomicCmpExchange是Delphi的内部函数,它的代码在被调用时是由编译器直接生成的,而糟糕的代码生成只会影响该过程,而不会影响通用代码——换句话说,匿名方法捕获在其他代码中正常工作(除非编译器中有其他与特定代码无关的错误(。

在RTL中使用AtomicCmpExchange的其他地方,没有涉及变量捕获的代码,因此RTL、VCL和FMX代码不受此问题的影响,可以在应用中启用优化。

注意:编译器中可能还有其他我们不知道的优化错误。

相关内容

  • 没有找到相关文章

最新更新