2017-02-27 10 views
1

からデータを取得し、このコードは問題ないはずです。デルファイTTask私はニック・ホッジスを読んで理解したところでは、メインスレッド

TTask.Run(
    procedure 
    var 
    resp, tmp: string; 
    req: boolean; 
    bwriter: TBinaryWriter; 
    myfile: TFileStream; 
    begin 
    //tell the user to wait 
    TThread.Queue(TThread.CurrentThread, 
     procedure 
     begin 
     LoginButton.Text := 'Please wait...'; 
     end 
    ); 

    //some checks 
    try 
     resp := GetURL('... here I get a result from the server...'); 
     if (resp = fOKstatus) then 
     begin 
     req := true; 

     myfile := TFileStream.Create(TPath.Combine(TPath.GetHomePath, 'docs.mkb'), fmCreate); 
     try 
      bwriter := TBinaryWriter.Create(myfile, TEncoding.Unicode, false); 
      try 
      bwriter.Write(UsernameEdit.Text); 
      bwriter.Write(AppIDEdit.Text); 
      bwriter.Close; 
      finally 
      bwriter.Free; 
      end; 
     finally 
      myfile.Free; 
     end; 
     end 
     else 
     begin 
     req := false; 
     end; 
    except 
     req := false; 
    end; 

    //final 
    TThread.Queue(TThread.CurrentThread, 
     procedure 
     begin 
     if (req = true) then 
     begin 
      LoginButton.Text := 'Success!'; 
      ShowMessage('Close the app to complete the registration.'); 
     end 
     else 
     begin 
      LoginButton.Text := 'Login failed.'; 
     end; 
     end 
    ); 

    end 
); 

これを分離したスレッドで実行され、それが呼び出しでメインスレッドにリンクされていますQueue()になります。実際、最初はこのメソッドを使用してButtonのTextを更新しています。


質問。これらの2行を見てください:

bwriter.Write(UsernameEdit.Text); 
bwriter.Write(AppIDEdit.Text); 

私は、メインスレッドのUIにある2つのエディットコントロールからユーザー名と(ランダムコードである)のAppIDを取得する必要があります。これは正しいです?

私はQueue()に電話するべきだと思いますが、今のところプログラムはうまくいきます。

このように値を安全に取ることはできますか?私は何も更新していない、そして、私はちょうどデータを取得する必要がありますが、私は2つの異なるタスクからコンテンツを混在させることが危険な/悪い習慣であるかどうかわかりません。

答えて

4

あなたが心配している2行のコードは、スレッドセーフではありません。あなたはメインスレッドと同期する必要がありますすべて読み取りと書き込みの両方のUIアクセス。 TThread.Queue()で、非同期であるため、UIから値を取得する目的には適していません。

TTask.Run(
    procedure 
    var 
    resp, tmp, username, appid: string; 
    req: boolean; 
    bwriter: TBinaryWriter; 
    myfile: TFileStream; 
    begin 
    //tell the user to wait 
    TThread.Queue(nil, 
     procedure 
     begin 
     LoginButton.Text := 'Please wait...'; 
     end 
    ); 

    //some checks 
    try 
     resp := GetURL('... here I get a result from the server...'); 
     if resp = fOKstatus then 
     begin 
     req := true; 

     TThread.Synchronize(nil, 
      procedure 
      begin 
      username := UsernameEdit.Text; 
      appid := AppIDEdit.Text; 
      end 
     ); 

     myfile := TFileStream.Create(TPath.Combine(TPath.GetHomePath, 'docs.mkb'), fmCreate); 
     try 
      bwriter := TBinaryWriter.Create(myfile, TEncoding.Unicode, false); 
      try 
      bwriter.Write(username); 
      bwriter.Write(appid); 
      bwriter.Close; 
      finally 
      bwriter.Free; 
      end; 
     finally 
      myfile.Free; 
     end; 
     end 
     else 
     begin 
     req := false; 
     end; 
    except 
     req := false; 
    end; 

    //final 
    TThread.Queue(nil, 
     procedure 
     begin 
     if req then 
     begin 
      LoginButton.Text := 'Success!'; 
      ShowMessage('Close the app to complete the registration.'); 
     end 
     else 
     begin 
      LoginButton.Text := 'Login failed.'; 
     end; 
     end 
    ); 
    end 
); 

また、メインUIスレッドを仮定すると、TTaskを開始するものです、あなたはTTaskを開始する前に、2つの値を読み、匿名プロシージャはそれらをキャプチャさせることができます:同期である、代わりにTThread.Synchronize()を使用

var 
    username, appid: string; 
begin 
    username := UsernameEdit.Text; 
    appid := AppIDEdit.Text; 

    TTask.Run(
    procedure 
    var 
     resp, tmp: string; 
     req: boolean; 
     bwriter: TBinaryWriter; 
     myfile: TFileStream; 
    begin 
     //tell the user to wait 
     TThread.Queue(nil, 
     procedure 
     begin 
      LoginButton.Text := 'Please wait...'; 
     end 
    ); 

     //some checks 
     try 
     resp := GetURL('... here I get a result from the server...'); 
     if resp = fOKstatus then 
     begin 
      req := true; 

      myfile := TFileStream.Create(TPath.Combine(TPath.GetHomePath, 'docs.mkb'), fmCreate); 
      try 
      bwriter := TBinaryWriter.Create(myfile, TEncoding.Unicode, false); 
      try 
       bwriter.Write(username); 
       bwriter.Write(appid); 
       bwriter.Close; 
      finally 
       bwriter.Free; 
      end; 
      finally 
      myfile.Free; 
      end; 
     end 
     else 
     begin 
      req := false; 
     end; 
     except 
     req := false; 
     end; 

     //final 
     TThread.Queue(nil, 
     procedure 
     begin 
      if req then 
      begin 
      LoginButton.Text := 'Success!'; 
      ShowMessage('Close the app to complete the registration.'); 
      end 
      else 
      begin 
      LoginButton.Text := 'Login failed.'; 
      end; 
     end 
    ); 
    end 
); 
end; 
+0

あなたは私の疑問を解決しました:)しかし、なぜあなたはTThread.CurrentThreadを呼び出さず、代わりにnilを使用していますか? –

+0

'TThread.Queue()'の最初のパラメータが 'nil'でなければ、待ち行列に入れられた操作は指定されたスレッドにリンクされ、そのスレッドが終了すると、処理されていなければ*キャンセル*自身の 'TThread.RemoveQueuedEvents()'を呼び出します)。 'Queue()'は非同期なので、注意しないと操作が不完全になる可能性があります。代わりに 'nil'を渡すことでその落とし穴を避けることができます。 'TThread.Synchronize()'に関しては、同じパラメータは基本的に冗長で無用です。どちらのメソッドにも 'nil'以外のポインタを渡す正当な理由はありませんでした。 –

+0

私はTThreadのサブクラスでExecuteを使っていても同じことを(nilを設定して)行うべきだと思いますか?そして、あなたのコメントで私が理解していることから、両方でnilを使うのが最善の選択でしょうか?非常に参考になりました。Remy btw、ありがとうございました –

関連する問題