如果您创建了一个新的多设备应用程序项目,请设置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代码不受此问题的影响,可以在应用中启用优化。
注意:编译器中可能还有其他我们不知道的优化错误。