为什么我的线程在表单创建后运行?

时间:2014-04-24 10:13:33

标签: multithreading delphi delay

我有一个项目,主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

6 个答案:

答案 0 :(得分:5)

穆罕默德,我认为这是 [多线程] 误解的典型例子。

您尝试使用代码总是好的,但您必须学习基础知识。 正如大卫所说,你违反了规则。整个概念。您可能会因为代码不起作用而感到沮丧,但由于您的挫败感,VCL将 突然变为线程安全。

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

Martin Harvey的一本书,Multithreading - The Delphi Way。对于真正的极客来说,阅读既简单又有趣。您可能不想超越互斥体,关键部分和并发控制,但至少您将学习一些关于多线程惊人世界的重要概念。

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:2)

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

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.

很抱歉,如果我误解了你的问题。

答案 3 :(得分:1)

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

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

如果您想确定首次显示另一个表单,您可以执行以下操作:

  1. 将另一个表单设为主表单并在关闭时显示当前主表单(或单击按钮,计时器用完等)
  2. 放置代码以在BeforeDestruction方法中显示另一个表单,以使其在表单的构造函数之前执行。这将使它首先显示,但它不会阻止主窗体在其后显示。
  3. 我希望这会对你有所帮助。

答案 4 :(得分:0)

问题:

(1)有没有办法从主窗体中运行我的线程?

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

(3)......显示主要表格时。

<强>反应:

(1)是的。从Main表单创建线程(不在Main表单的Oncreate事件中)。

(2)好的。 (同等回应(1))。在创建Main表单之前进行表单创建(或Thread)(而不是在Main Form的OnCreate事件中)。

(3)这对于(1)和(2)是不可能的。抱歉。

答案 5 :(得分:-2)

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.