multithreading - Delphi TTask 从主线程获取数据

标签 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() 链接到主线程。事实上,一开始我正在使用此方法更新按钮的Text


问题。看一下这两行:

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

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

我想我应该调用Queue(),但到目前为止程序运行良好。

我可以安全地以这种方式获取这些值吗?我没有更新任何内容,我只需要获取数据,但我不确定混合来自两个不同任务的内容是否会是危险/不好的做法。

最佳答案

您关心的两行代码不是线程安全的。您必须与主线程同步才能进行所有 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;

关于multithreading - Delphi TTask 从主线程获取数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42496767/

相关文章:

string - 如何从对象检查器中使用 sLineBreak?

delphi - 具有参数约束的泛型构造函数?

delphi - 以这种方式删除和重新分配事件安全吗?如果没有,为什么?

multithreading - 哪个更高效?更多内核或更多 CPU

c++ - 防止竞争条件

具有处理程序和线程的 Android 服务

delphi - delphi 如何将大端数字转换为 native 数字

windows - 如何检测 Delpi 代码是否在 DLL 中运行?

python - pygtk 应用程序中的单独线程

c# - 两个线程使用相同的变量,会产生问题