【问题标题】:Why my Thread run after Form creation?为什么我的线程在创建表单后运行?
【发布时间】:2014-04-24 10:13:32
【问题描述】:

我有一个项目,主 Form 是在我的 Thread 之后创建的。 但是这段代码不能正常工作:

type
  TMyThread = class(TThread)
  public
    procedure Execute; override;
    procedure doProc;
  end; { type }
.
.
.
procedure TForm1.FormCreate(Sender: TObject);
var
  thrd : TMyThread;
begin
  thrd := TMyThread.Create(True);
  thrd.Resume;

  // Following code will cause the `Form` to show the time delay is about 5 seconds...
end;
.
.
.

procedure TMyThread.Execute;
begin
  inherited;

  doProc;
end;

procedure TMyThread.doProc;
var
  AForm : TForm;
begin
  AForm := TForm.Create(nil);
  AForm.Caption := 'Thread Form';
  AForm.Position := poScreenCenter;
  AForm.FormStyle := fsStayOnTop;
  AForm.Show;
end;

我不想使用Synchronize。 有没有办法让我的Thread 脱离主要的Form? 我想在主Form 开始创建之前显示Form,同时显示主Form

【问题讨论】:

  • 如果您想在创建主要表单之前创建并显示一些表单,请执行此操作。但不是来自工作线程!
  • 不,我不想要启动画面。
  • 我的线程表单做一些处理。我没有在代码中解释这一点。因为我的问题很明确。
  • 不由我说。尽管您最近的所有问题都得到了答案,但就 VCL 表单和线程而言,您似乎仍然没有“明白”,因此阅读该主题可能不会对您造成任何伤害..
  • 没有。您问的问题是为什么您的代码不能按您期望的方式工作。我这样回答。至于如何在线程中运行 GUI,我也回答了。

标签: multithreading delphi delay


【解决方案1】:

Mohammad,我认为这是total [多线程] 误解的经典示例。

尝试自己的代码总是好的,但您必须学习基础知识。 正如大卫所说,你违反了规则。整个概念。你可能会因为你的代码不工作而沮丧一千次,但是 VCL不会因为你的沮丧而突然变成线程安全的。

我的建议是RTFM。有一个很棒的。

Martin Harvey 的一本书,Multithreading - The Delphi Way。对于真正的极客来说,阅读既简单又有趣。你可能不想超越互斥锁、临界区和并发控制,但至少你会学到一些关于多线程神奇世界的非常重要的概念。

请不要指望 SO 的任何人回答错误的问题。大多数响应者都是专业且经验丰富的程序员,他们不想浪费时间深入讨论最初错误的讨论。这里的人都是布尔型的;)

换句话说,在发布问题之前尝试学习一些非常基本的东西。

【讨论】:

    【解决方案2】:

    您的代码违反了 VCL 线程规则。所有 VCL 访问都必须在主线程上。

    如果您想在不同的线程中显示 GUI(这很少是一个好主意),您需要使用原始 Win32 API 调用。并在线程中运行消息循环。

    我不能告诉你如何解决你的问题,因为我不知道你的问题是什么。但是,如果您只想在主表单之前显示一个表单,那就这样做吧。没有明显的需要线程。

    【讨论】:

    • 调用 CreateWindow。我们很难在不知道问题的情况下提出解决方案。
    【解决方案3】:

    这可能太明显了,但你为什么不在表单创建之前运行你的代码呢? (其他人已经提出了类似的建议) 如果您创建一个新项目并查看该项目的源代码,它将如下所示:

    program Project1;
    
    uses
      Forms,
      Unit1 in 'Unit1.pas' {Form1};
    
    {$R *.RES}
    
    begin
      (* Insert your code in here so it runs before the mainform is created *) 
    
      Application.Initialize;
      Application.CreateForm(TForm1, Form1);
      Application.Run;
    end.
    

    对不起,如果我误解了你的问题。

    【讨论】:

    • 谢谢,但这不是我的答案!
    • @Mohammadreza 您问为什么您的代码不起作用。答案是您违反了 VCL 线程规则。请不要指望我们会为您未说明的问题提供解决方案。
    • 好吧,我的问题是:在创建Form 之前,我需要一个独立工作的线程。我想在Thread做一些代码。
    • @Mohammadreza,无法在线程中创建和运行 VCL 表单。
    • 你不应该在Application.Initialize 之后Application.CreateForm之前之前运行线程代码吗?
    【解决方案4】:

    回答“为什么我的线程在创建表单后运行?”这个问题 在TCustomForm.Create 中,DoCreate 被调用,这会触发OnCreate 事件。 此时表单本身已经创建完毕,将在OnCreate 事件返回后立即显示。

    在您的情况下,您在此事件期间创建线程。 调用Resume 只会告诉线程它可以执行,但不能保证立即执行(而且它会有问题,正如大卫解释的那样)。 DoCreate 可能会在调用 Execute 方法之前完成。 这也意味着 Synchronize 将无济于事,因为它将在 Execute 上下文中调用。

    如果您想确定首先显示另一个表单,您可以做几件事:

    1. 将另一个窗体设为主窗体,并在其关闭(或单击按钮、计时器用完等)时显示当前主窗体
    2. 将显示另一个窗体的代码放在BeforeDestruction 方法中,使其在窗体的构造函数之前执行。这将使其首先显示,但不会阻止主窗体立即显示。

    希望对你有帮助。

    【讨论】:

      【解决方案5】:

      问题:

      (1) 有没有办法让我的线程脱离主窗体?

      (2) 我想在主Form开始创建之前显示一个Form ...

      (3) ...同时显示主窗体。

      回应:

      (1) 是的。在主窗体之外创建线程(不在主窗体的 Oncreate 事件内)。

      (2) 好的。 (与(1)相同)。在创建主窗体之前进行窗体创建(或线程)(不在主窗体的 OnCreate 事件中)。

      (3) 这在 (1) 和 (2) 中是不可能的。 抱歉。

      【讨论】:

        【解决方案6】:
        unit AniThread;
        
        interface 
        
        uses 
          Classes, Windows, Controls, Graphics; 
        
        type 
          TAnimationThread = class(TThread) 
          private 
            { private declarations } 
            FWnd: HWND; 
            FPaintRect: TRect; 
            FbkColor, FfgColor: TColor; 
            FInterval: integer; 
          protected 
            procedure Execute; override; 
          public 
            constructor Create(paintsurface : TWinControl; {Control to paint on } 
              paintrect : TRect;          { area for animation bar }
              bkColor, barcolor : TColor; { colors to use }
              interval : integer);        { wait in msecs between paints}
          end; 
        
        implementation 
        
        constructor TAnimationThread.Create(paintsurface : TWinControl; 
          paintrect : TRect; bkColor, barcolor : TColor; interval : integer); 
        begin 
          inherited Create(True); 
          FWnd := paintsurface.Handle; 
          FPaintRect := paintrect; 
          FbkColor := bkColor; 
          FfgColor := barColor; 
          FInterval := interval; 
          FreeOnterminate := True; 
          Resume; 
        end; { TAnimationThread.Create } 
        
        procedure TAnimationThread.Execute; 
        var 
          image : TBitmap; 
          DC : HDC; 
          left, right : integer; 
          increment : integer; 
          imagerect : TRect; 
          state : (incRight, incLeft, decLeft, decRight); 
        begin 
          Image := TBitmap.Create; 
          try 
            with Image do  
            begin 
              Width := FPaintRect.Right - FPaintRect.Left; 
              Height := FPaintRect.Bottom - FPaintRect.Top; 
              imagerect := Rect(0, 0, Width, Height); 
            end; { with } 
            left := 0; 
            right := 0; 
            increment := imagerect.right div 50; 
            state := Low(State); 
            while not Terminated do  
            begin 
              with Image.Canvas do  
              begin 
                Brush.Color := FbkColor; 
                FillRect(imagerect); 
                case state of 
                  incRight:  
                  begin 
                    Inc(right, increment); 
                    if right > imagerect.right then  
                    begin 
                      right := imagerect.right; 
                      Inc(state); 
                    end; { if } 
                  end; { case incRight } 
                  incLeft:  
                  begin 
                    Inc(left, increment); 
                    if left >= right then  
                    begin 
                      left := right; 
                      Inc(state); 
                    end; { if } 
                  end; { case incLeft } 
                  decLeft:  
                  begin 
                    Dec(left, increment); 
                    if left <= 0 then  
                    begin 
                      left := 0; 
                      Inc(state); 
                    end; { if } 
                  end; { case decLeft } 
                  decRight:  
                  begin 
                    Dec(right, increment); 
                    if right <= 0 then  
                    begin 
                      right := 0; 
                      state := incRight; 
                    end; { if } 
                  end; { case decLeft } 
                end; { case } 
                Brush.Color := FfgColor; 
                FillRect(Rect(left, imagerect.top, right, imagerect.bottom)); 
              end; { with } 
              DC := GetDC(FWnd); 
              if DC <> 0 then 
                try 
                  BitBlt(DC, 
                    FPaintRect.Left, 
                    FPaintRect.Top, 
                    imagerect.right, 
                    imagerect.bottom, 
                    Image.Canvas.handle, 
                    0, 0, 
                    SRCCOPY); 
                finally 
                  ReleaseDC(FWnd, DC); 
                end; 
              Sleep(FInterval); 
            end; { while } 
          finally 
            Image.Free; 
          end; 
          InvalidateRect(FWnd, nil, True); 
        end; { TAnimationThread.Execute } 
        
        end. 
        
        //============================ HOW to USE IT ============================
        {
         Usage: 
         Place a TPanel on a form, size it as appropriate.Create an instance of the 
         TanimationThread call like this: procedure TForm1.Button1Click(Sender : TObject); } 
        var 
          ani : TAnimationThread; 
          r : TRect; 
        begin
          r := Panel1.ClientRect;
          InflateRect(r, - Panel1.BevelWidth, - Panel1.BevelWidth);
          ani := TAnimationThread.Create(Panel1, r, Panel1.color, clBlue, 25);
          Button1.Enabled := False; 
          Application.ProcessMessages; 
          Sleep(30000);  // replace with query.Open or such 
          Button1.Enabled := True; 
          ani.Terminate; 
          ShowMessage('Done'); 
        end.
        

        【讨论】:

        • 已编辑,解决办法
        • -1,这段代码可能正在做你想做的事,但这不是问题的答案(至少就我能够理解的问题而言)。
        • 问题出在 VCL 模式下。因为我看不到任何结果。永远,这是我的解决方案。
        • 致电Application.ProcessMessages 是一个非常糟糕的主意。添加您使用多个线程并且是灾难的邀请的事实。好像你想WaitForSingleObject 超时 30 秒。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-07-24
        • 1970-01-01
        • 1970-01-01
        • 2015-08-16
        • 1970-01-01
        • 2013-01-12
        • 1970-01-01
        相关资源
        最近更新 更多