【问题标题】:Delphi TTask get data from main threadDelphi TTask 从主线程获取数据
【发布时间】:2017-02-28 06:48:22
【问题描述】:

根据我对 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(),但目前程序运行良好。

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

【问题讨论】:

    标签: multithreading delphi


    【解决方案1】:

    您关心的 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 之前读取这两个值并让匿名过程捕获它们:

    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;
    

    【讨论】:

    • 好吧,你解决了我的疑问 :) 但是为什么你不调用 TThread.CurrentThread 而是使用 nil 呢?
    • TThread.Queue()的第一个参数不是nil时,队列中的操作被链接到指定的线程,当该线程终止时操作被取消尚未处理(线程在自身上调用TThread.RemoveQueuedEvents())。由于Queue() 是异步的,如果您不小心,可能会导致未执行的操作。通过nil 可以避免这个陷阱。至于TThread.Synchronize(),同样的参数基本是多余的,没用的。我从来没有充分的理由将非nil 指针传递给这两种方法。
    • 我想即使我在 TThread 的子类中使用 Execute,我也应该这样做(设置 nil)?根据我在您的评论中的理解,在两者中使用 nil 是最好的选择,对吗?非常有帮助的雷米顺便说一句,谢谢你
    • TThread派生类中,一般不会使用Queue()/Synchronize()的静态版本,而是使用没有AThread参数的实例版本因为他们依赖Self 指针。当从TThread 类(例如TTask,或在非Delphi 线程中)调用时,应使用静态版本。
    • 好的,所以我只有在使用 TTasks 时才必须使用 nil,而不是在使用派生时。再次感谢;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 2014-06-28
    • 2020-05-28
    相关资源
    最近更新 更多