Delphi TTask从主线程获取数据

时间:2017-02-27 22:14:36

标签: multithreading delphi

据我所知,阅读Nick Hodges,这段代码应该没问题:

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

问题。看看这两行:

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

我需要从主线程UI中的两个Edit控件中检索用户名和AppID(这是一个随机代码)。这是对的吗?

我想我应该致电Queue(),但到目前为止该计划运作良好。

我可以安全地以这种方式获取值吗?我没有更新任何东西,我只需要抓取数据,但我不确定混合2个不同任务的内容是否可能是危险/不良做法。

1 个答案:

答案 0 :(得分:4)

您关注的2行代码不是线程安全的。您必须所有 UI访问的主线程同步,包括读取和写入。 TThread.Queue()异步,因此不适合从UI中检索值。请改为使用TThread.Synchronize(),即同步

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个值并让匿名过程捕获它们:

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;