如果你新建一個多設備應用專案,設定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.
為什么此代碼會在 Win32 上崩潰if FKey <> LCompareKey then raise Exception.Create('Error 2');?
我正在使用Delphi 10.4 悉尼更新 3。我還沒有在 Delphi 11 Alexandria 中嘗試過,所以我不知道它是否在那個版本中作業。
除了停用優化之外,還有其他解決方法嗎?
另一個問題 - 激活優化真的安全嗎?
uj5u.com熱心網友回復:
是的,AtomicCmpExchange當優化打開時,Win32 編譯器上的codegen for被破壞。
問題與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;
另一個不太理想的解決方案是Button1Click使用{$O-}編譯器指令關閉對損壞方法的優化,然后使用編譯器指令將其重新打開{$O }
由于AtomicCmpExchange是 Delphi 內在函式,它的代碼在呼叫時由編譯器直接生成,并且錯誤的代碼生成僅影響該程序,而不是通用代碼 - 換句話說,匿名方法捕獲在其他代碼中正常作業(除非編譯器中存在其他錯誤) ,與這個特定的無關)。
在RTL中其他使用的地方,AtomicCmpExchange沒有涉及到變數捕獲的代碼,所以RTL、VCL和FMX代碼不受這個問題的影響,可以在應用中開啟優化。
注意:編譯器中可能還有其他我們不知道的優化錯誤。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/365476.html
