在启动软件时,需要打开多个窗体,且每个窗体的打开时间都较长,因此为了界面友好,首先打开主窗体,并在主窗体上设置了进度条,在进度条不断滚动的情况下,使用多线程类打开其他窗体。问题是,使用多线程类打开其他窗体时软件总是死机。主窗体界面如下:
主窗体代码如下:
unit uInitForms;interfaceuses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, ADODB, Buttons;type
  TfrmInitForms = class(TForm)
    GroupBox1: TGroupBox;
    lblInMainList: TLabel;
    lblInSearch: TLabel;
    lblOutMainList: TLabel;
    lblBillManage: TLabel;
    pbInMainList: TProgressBar;
    pbInSearch: TProgressBar;
    pbOutMainList: TProgressBar;
    pbBillManage: TProgressBar;
    Timer1: TTimer;
    pbOutSearch: TProgressBar;
    lblOutSearch: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure btnUploadClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;var
  frmInitForms: TfrmInitForms;implementation{$R *.dfm}
uses udatamodule, comm2, uInitForms_thread, uInMainList;procedure TfrmInitForms.Timer1Timer(Sender: TObject);
begin
  if pbInMainList.Position>=pbInMainList.Max then
  begin
    pbInMainList.Position:=0;
  end
  else
  begin
    pbInMainList.Position:=pbInMainList.Position + pbInMainList.Step;
  end;
end;end.多线程类的代码如下:
unit uInitForms_Thread;interfaceuses
  Classes, StdCtrls, ComCtrls, ExtCtrls, Graphics, ADODB, SysUtils, Forms;//Windows, , Forms;
  type
  InitForms_Thread = class(TThread)
  private
    procedure InitInMainList();
    procedure InitOutMainList();
    procedure InitInSearch();
    procedure InitOutSearch();
    procedure InitBillManage();
  protected
  public    lblInMainList: TLabel;
    LblInSearch: TLabel;
    lblOutMainList: TLabel;
    lblOutSearch: TLabel;
    lblBillManage: TLabel;
    pbInMainList: TProgressBar;
    pbInSearch: TProgressBar;
    pbOutMainList: TProgressBar;
    pbOutSearch: TProgressBar;
    pbBillManage: TProgressBar;
    Timer1: TTimer;
    constructor create;
    procedure Execute; override;
  end;implementationuses 
  uInitForms, udatamodule, comm2, uInMainList, uInSearch, 
  uOutMainList, uOutSearch, uBillManage;procedure InitForms_thread.Execute;
begin
  timer1:=uInitForms.frmInitForms.Timer1;  lblInMainList:=uInitForms.frmInitForms.lblInMainList;
  pbInMainList:=uInitForms.frmInitForms.pbInMainList;  lblInSearch:=uInitForms.frmInitForms.lblInSearch;
  pbInSearch:=uInitForms.frmInitForms.pbInSearch;  lblOutMainList:=uInitForms.frmInitForms.lblOutMainList;
  pbOutMainList:=uInitForms.frmInitForms.pbOutMainList;  lblOutSearch:=uInitForms.frmInitForms.lblOutSearch;
  pbOutSearch:=uInitForms.frmInitForms.pbOutSearch;  lblBillManage:=uInitForms.frmInitForms.lblBillManage;
  pbBillManage:=uInitForms.frmInitForms.pbBillManage;  pbInMainList.Position:=0;
  pbInSearch.Position:=0;
  pbOutMainList.Position:=0;
  pbOutSearch.Position:=0;
  pbBillManage.Position:=0;  self.InitInMainList;
  self.InitInSearch;
  self.InitOutMainList;
  self.InitOutSearch;
  self.InitBillManage;
end;constructor InitForms_thread.create;
begin
  FreeOnTerminate:=true;
  inherited create(false);
end;procedure  InitForms_thread.InitInMainList();
begin
  lblInMainList.Font.Color:=clRed;
  lblInMainList.Caption:='正在初始化当日收货信息列表';
  timer1.Enabled:=true;
  //开始创建窗体
  if frmInMainList = nil then
  begin
    frmInMainList := TfrmInMainList.Create(Application );
  end;
  //创建窗体后,最小化窗体
  frmInMainList.WindowState:=wsMinimized;
  pbInMainList.Position:=pbInMainList.Max;
  timer1.Enabled:=false;
  lblInMainList.Font.Color:=clgreen;
  lblInMainList.Caption:='当日收货信息列表初始化完成';
end;procedure  InitForms_thread.InitInSearch();
begin
  lblInSearch.Font.Color:=clRed;
  lblInSearch.Caption:='正在初始化收货查询数据';
  timer1.Enabled:=true;
  //开始创建窗体
  if frmInSearch = nil then
  begin
    frmInSearch := TfrmInSearch.Create(Application );
  end;
  //创建窗体后,最小化窗体
  frmInSearch.WindowState:=wsMinimized;
  pbInSearch.Position:=pbInSearch.Max;
  timer1.Enabled:=false;
  lblInSearch.Font.Color:=clgreen;
  lblInSearch.Caption:='收货查询数据初始化完成';
end;procedure  InitForms_thread.InitOutMainList();
begin
  //红色字体提示
  lblOutMainList.Font.Color:=clRed;
  lblOutMainList.Caption:='正在初始化当日发货信息列表';
  timer1.Enabled:=true;
  //开始创建窗体
  if frmOutMainList = nil then
  begin
    frmOutMainList := TfrmOutMainList.Create(Application );
  end;
  //创建窗体后,最小化窗体
  frmOutMainList.WindowState:=wsMinimized;
  //填满进度条
  pbOutMainList.Position:=pbOutMainList.Max;
  //停止进度条滚动
  timer1.Enabled:=false;
  //绿色字体提示
  lblOutMainList.Font.Color:=clgreen;
  lblOutMainList.Caption:='当日发货信息列表初始化完成';
end;procedure  InitForms_thread.InitOutSearch();
begin
  lblOutSearch.Font.Color:=clRed;
  lblOutSearch.Caption:='正在初始化发货查询数据';
  timer1.Enabled:=true;
  //开始创建窗体
  if frmOutSearch = nil then
  begin
    frmOutSearch := TfrmOutSearch.Create(Application );
  end;
  //创建窗体后,最小化窗体
  frmOutSearch.WindowState:=wsMinimized;
  pbOutSearch.Position:=pbOutSearch.Max;
  timer1.Enabled:=false;
  lblOutSearch.Font.Color:=clgreen;
  lblOutSearch.Caption:='发货查询数据初始化完成';
end;procedure  InitForms_thread.InitBillManage();
begin
  lblBillManage.Font.Color:=clRed;
  lblBillManage.Caption:='正在初始化单据管理数据';
  timer1.Enabled:=true;
  //开始创建窗体
  if frmBillManage = nil then
  begin
    frmBillManage := TfrmBillManage.Create(Application );
  end;
  //创建窗体后,最小化窗体
  frmBillManage.WindowState:=wsMinimized;
  pbBillManage.Position:=pbBillManage.Max;
  timer1.Enabled:=false;
  lblBillManage.Font.Color:=clgreen;
  lblBillManage.Caption:='单据管理数据初始化完成';
end;end.

解决方案 »

  1.   

    InitForms_thread.Execute;这个过程可能有问题,仔细查查。
      

  2.   

    把如下创建窗体的代码
      //开始创建窗体 
      if frmInMainList = nil then 
      begin 
        frmInMainList := TfrmInMainList.Create(Application ); 
      end; 
      //创建窗体后,最小化窗体 
      frmInMainList.WindowState:=wsMinimized;
    替换成连接数据库等不涉及窗体的代码后,是可以正常运行的。不知为什么不能在多线程类中创建窗体
    跟踪了一下代码,是canvas引起的,既不报错也不提示,直接就死机,郁闷啊!
      

  3.   

    肯定不是TCanvas引起的。delphi5开发人员指南 多线程一章中可以看到:VCL要求所有的用户界面控制要发生在一个应用程序的主线程的环境中(线程 安全的TCanvas类除外)。
    建议你下来看一下。
      

  4.   

    回复3楼:   可是对我的代码确实是Canvas引起的死机啊,代码页不复杂,你测试下啊。                  谢谢啦
      

  5.   

    给你的线程新增加ChildCreate的过程:
    写了个简单示例,你可以试一下。主要要用 Synchronize() 来进行线程 同步。当然了,也可以用sendmessage或者postMessage。Delphi5开发人员指南中都有。
    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        btn1: TButton;
        procedure btn1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      Form1: TForm1;implementationuses Unit2;{$R *.dfm}procedure TForm1.btn1Click(Sender: TObject);
    var
      a : aaa;
    begin
      a := aaa.Create(False);
    end;end.unit Unit2;interfaceuses
      Classes;type
      aaa = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
        procedure ChildCreate;
      end;implementation{ aaa }uses Unit3,Unit1;procedure aaa.ChildCreate;
    begin
      if   not   Assigned(Form3)   then
          Form3:=   TForm3.Create(Form1);
      Form3.Show;
    end;procedure aaa.Execute;
    begin
      { Place thread code here }
      Synchronize(ChildCreate);
    end;end.   
      

  6.   

    Vcl中开发,涉及到用户界面的操作 必须在主线程 内进行。
    线程中通过同步方法来实现更新。
      

  7.   

    To chhrsas:
      Thanks, 我根据你的思路试试。
      

  8.   

    To chhrsas: 
      Thanks, 你的方法试过了,使用你的方法后,程序不再死机了,但在创建其他窗体的同时进度条也假死了,还是没有起到多线程的目的啊。
      

  9.   

    Vcl中开发,涉及到用户界面的操作 必须在主线程 内进行。
    线程中通过同步方法来实现更新。
    也就是说 你在Execute的方法里用同步方法,如(Synchronize()、sendmessage()、postmessage())对主线程中对象进行更新。
      

  10.   

    To chhrsas: 
      Thanks, 我的目的是在创建其他窗体的时候,让进度条滚动起来,使得界面友好些,使用 Synchronize(ChildCreate)后,虽然不死机了,但在创建其他窗体的时候,界面还是静止的。
       你有什么较好的方法呢?
      

  11.   

    如在Execute里用Synchronize()方法同步完窗体的创建后,继续用这个方法进行进度条的同步。
    按上面的例子改了下。
    procedure aaa.Execute;
    begin
      { Place thread code here }
      Synchronize(ChildCreate);
      repeat
        Synchronize(updateMMo);
        sleep(1000);
      until SizeOf(Form3.mmo1.Lines) >  1000;
    end;procedure aaa.updateMMo;
    begin
      Form3.mmo1.Lines.Add('aabbbccc');
    end;如果多个线程更新同一个进度条,则需要用到临界区 进行多线程 间的同步。具体的你可以查一下。
      

  12.   

    多线程间的同步,在 Main窗体里 创建临界区
    如,先在主窗体里 定义一个变量 cs : TRTLCriticalSection;
    再在btn1点击时,初始化 InitializeCriticalSection(cs);
    在线程 Execute;更新主线程对像方法中加上进临界区和出临界区
    EnterCriticalSection(cs);
    Synchronize(updateMMo);
    LeaveCriticalSection(cs);
    最后,别忘了关掉临界区。
    DeleteCriticalSection(cs);
    在做临界区更新时,一定要每个线程都要进行临界区保护。
      

  13.   

    To chhrsas: 
      Thanks, 
      你的方法试过了,还是不行啊,
      Synchronize(ChildCreate);
      执行完之后,才执行
      repeat
        Synchronize(updateMMo);
        sleep(1000);
      until SizeOf(Form3.mmo1.Lines) >  1000;所以还是,进度条还是静止的啊真郁闷
      

  14.   

    你可以试着不在创建窗体里 初始化数据。把初始化数据的过程放到 循环里。
    同时去掉线程里的Timer.
      

  15.   

    已经去掉timer了,创建窗体里没有初始化数据,是初始化窗体的配置了,因为窗体上好动控件都是配置后保存在数据库里的,所以需要在创建窗体时初始化界面。
      

  16.   

    那好办啊。在主窗体中用timer控件进行 进度条的更新。线程只管初始化数据。
      

  17.   

    没有看源代码
    我觉得可以用beginthread来写创建过程可以灵活点
      

  18.   

    首先不应该在线程中直接操作窗体,也决对不要的线程中直接操作界面控件。你在线程中定义这些界面是控
    件也不可以。Type
      TMyThread=class(TThread)
      private
        FMaxProgress:Integer;//进度的最大值
        FProgress:Integer//当前的进度值
      published
        property MaxProgress:Integer read FMaxProgress;
        property Progress:Integer read FProgress;
      end;
      
      在开始执行线程的时候将MaxProgres设置好,在执行的过程中修改 Progress 。
      在界面上你放一个 TTimer控件,和5个进度条,每秒执行一次
      procedure TForm1.OnTime(Sender:TObject)
      begin
        //这里读取每个线程的 MaxProgres 和 Progress 给进度条赋值。
        
      end;
      //你可以在另外放一个 TTimer tmr_show ,初始设置Enabled=false ,Interval=500;
      //在窗体的 
      procedure TForm1.FormShow(Sender: TObject);
      begin
         tmr_show.Enabled:=True;
      end;
      
      procedure TForm1.tmr_showTimer(Sender: TObject);
      begin
      //在这里创建线程。
      end;
      

  19.   

    回复22楼   首先感谢你的关注。
       我说一下我的问题,现在的程序的问题是,用户打开每个界面时速度较慢(共有五个界面),为了解决这个问题,我想在程序启动时,就先把这五个窗体加载上,然后把windowstate属性设为wsMinimized,这个当用户再次打开时,速度就比较快了。   在程序启动时,加载这五个窗体需要一段时间,现在想在用户等待这段时间的过程中,显示一个动画界面。但是这个动画界面总是无法实现。  该如何解决呢?   谢谢
      

  20.   

    回复22楼procedure TForm1.OnTime(Sender:TObject)这个是什么事件呢
      

  21.   

    procedure TForm1.OnTime(Sender:TObject) 意思是你在界面上在放上一个 TTimer ,它定时的读取每个线程的当前值,给界面年是进度条设置。打开界面的速度慢先要分析你在窗体的 change show 事件中执行了什么,是否有必要在窗体一显示出来的时候就加载很多的数据。业务确实有需要在启动的时候打开多个窗体吗?这些都要根据你的程序的具体业务来分析。
      

  22.   

    回复25楼我的程序,在打开窗体时,要加载DBgridEh,DBGridEh一共有70列,每列的配置都保存在数据库中,因此,打开界面时,先从数据库中读取数据,然后根据数据来初始化各列,包括:列是否显示、列标题、列宽度、列字段名,这个样速度就很慢。你有啥好的建议呢?谢谢
      

  23.   

    主要是初始化DBGridEh的数据列耗时太多,加载数据行并不耗时。问题是不同的用户对于见面的配置不同,我的程序得适用于各种用户,所以70多个数据列要根据不同的用户配置来初始化。谢谢您的关注!
      

  24.   

    可以这样理解这个问题   创建两个窗体。   一个窗体Form1,窗体上有一个ProgressBar、一个Timer、一个Button,由Timer的OnTimer事件来控制ProgressBar的position,每0.2秒position+1,由button的OnClick事件来打开Form1。   另一个窗体Form2,在FormShow事件中有大量的数据运算。      首先打开Form1,然后点击其上的Button来打开Form2.   请教高手们,如何使得在打开form2的同时,使得Form1的ProgressBar不假死呢?
       
      

  25.   

    http://download.csdn.net/source/2057561 写了一个小例子。