我想使用TThread类
启动5个线程,向listbox内写入1-100000的数值.
如何解决VCL和全局变量同步的问题呢?
能不能帮我写个Demo.

解决方案 »

  1.   


    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, SyncObjs;type
      TForm1 = class(TForm)
        Button1: TButton;
        ListBox1: TListBox;
        procedure FormCreate(Sender: TObject);
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;  TMyThread = class(TThread)
      public
        FCS: TCriticalSection;
        procedure Execute; override;
        constructor Create;
        destructor Destroy; override;
      end;var
      Form1: TForm1;  i: Integer;implementation{$R *.dfm}{ TMyThread }constructor TMyThread.Create;
    begin
      FCS := TCriticalSection.Create;
      self.FreeOnTerminate := True;
      inherited Create(False);
    end;destructor TMyThread.Destroy;
    begin
      FCS.Free;
      inherited;
    end;procedure TMyThread.Execute;
    begin
      inherited;
      while i < 100000 do
      begin
        FCS.Enter;
        i := i + 1;
        Form1.ListBox1.Items.Add(IntToStr(i) + ':' + IntToStr(Handle));
        //加上Handle可以看出是哪个线程添加的值
        FCS.Leave;
      end;
    end;procedure TForm1.FormCreate(Sender: TObject);
    begin
      i := 0;
    end;procedure TForm1.Button1Click(Sender: TObject);
    var
      a: TMyThread;
    begin
      a := TMyThread.Create;
      a := TMyThread.Create;
      a := TMyThread.Create;
      a := TMyThread.Create;
      a := TMyThread.Create;
    end;end.
      

  2.   


    兄弟, 首先线程这样用是明显的误区, 你的工作很简单, 完全是操作VCL对象, 这样的情况下必须要和VCL同步才可以, 如果是5个线程完全是浪费, 因为他们都被同步到主线程中去执行了. 如果你只是学使用方法, 2楼的帖子也是明显错误的, 坏的情况下是会导致死机的, 对于线程与VCL控件之间的协同必须交给Synchronize来做, 这个已经是常识了.
      

  3.   

    顺便说一下, 对于你的这个问题我建议直接写在主线程里, 不要再开额外线程, 如果你怕锁死界面, application.processmessage 在这里正是用的时机.
      

  4.   

    代码有误?求解释:) 
    我的经验是利用临界区加锁和解锁配对就不会出现死锁, Synchronize无非也就是EnterCriticalSection
    处理方法完毕后再LeaveCriticalSection
      

  5.   


    首先, 对于临界区的操作应该是防止多线程共享访问带来的灾难, 可是你把临界区放在线程内部当作一个成员变量, 那么每个线程访问的都是自己的临界区对象, 这样根本不存在同步, 也就是说这样做是毫无用处的, 其次是对于VCL的对象操作一直我们都说同步这是一种小误区, 实际应该说VCL的对象操作都需要交给主线程操作才对, 所以大家才会有误区认为线程同步好了就算是ok了, 真正理解还是需要仔细想想的, 换句话来说我们大家来想想对于一个vcl对象, 主线程随时都能访问它, 你怎么保证另外一个线程操作它时主线程不能访问它??方法只有一个, 就是把操作它的代码干脆交给主线程来做. 这是最 干净的办法.
      

  6.   

    procedure TMyThread.Execute;
    begin
      inherited;
      while i < 100000 do
      begin
        FCS.Enter;
        i := i + 1;
        Form1.ListBox1.Items.Add(IntToStr(i) + ':' + IntToStr(Handle));
        //加上Handle可以看出是哪个线程添加的值
        FCS.Leave;
      end;
    end;我们看一下这段代码, 这个时候你可以在界面上加一个按钮, 按钮里写Form1.ListBox1.Items.Add('111'); 线程在执行时如果不是使用Synchronize的话,主线程是可以随时对Form1.listbox1操作的,为什么, 因为这里没有任何一句代码可以告诉我们FCS.Enter执行后Listbox1是不能被别人操作的, 正确的使用临界区也只是保证   FCS.Enter;
        i := i + 1;
        Form1.ListBox1.Items.Add(IntToStr(i) + ':' + IntToStr(Handle));
        //加上Handle可以看出是哪个线程添加的值
        FCS.Leave;这里面的代码不会并行执行, 但是无法保证ListBox1不会被别人操作, 就像主线程随时可以操作它
      

  7.   

    至少一个 不妥当的用法:
    在FCS.Enter后,没有使用 Try ... Finally FCS.Leave End
      

  8.   

    楼上的意思是说凡是想要用Enter .. Leave 保护的对象,在所有需要访问时,都要加上Enter .. Leave
      

  9.   

    学习ing!
    由8楼的代码能看出Handle是那个线程的吗?
      

  10.   

    首先,谢谢各位能够来到这里看我的问题.
    先声明一下.我只是为了学习动态创建多个线程写入VCL的方法.
    还有2楼的朋友,也谢谢你,我的要求是动态创建5个线程,让这5个线程写入1-100000的值
    而不是创建5个线程分别向listbox内写入1-100000的值.此贴只是为了学习动态创建多个线程的方法,还希望各位能够给予帮助!
      

  11.   

    顶上去,希望高手能够给出一个Demo学习!
      

  12.   

    嗯,1F代码的确漏洞百出!
    既然已经确定输出10000,那就用10000/5,每个线程分配一个起始累加数的地址,让各个线程去默默循环累加并把每个数记在各自线程中的一个TStringList中.在线程的onterminate中把该List赋给主线程中的LISTVIEW不就OK了?
      

  13.   


    unit Unit13;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, SyncObjs;type
      INumberGenerator = interface
        ['{C44CFFF2-D977-4388-B37D-F2BE324C967A}']
        function Finished : Boolean;
        function GenerateNextNumber: Integer;
      end;  TForm13 = class(TForm, INumberGenerator)
        btn1: TButton;
        lst1: TListBox;
        btn2: TButton;
        btn3: TButton;
        procedure FormCreate(Sender: TObject);
        procedure btn1Click(Sender: TObject);
        procedure btn2Click(Sender: TObject);
        procedure btn3Click(Sender: TObject);  private
        fNumber : integer;    { Private declarations }
      public
        function GenerateNextNumber: Integer;
        function Finished: Boolean;
        { Public declarations }
      end;  TNumExportThread = class(TThread)
      private
        fList: TStrings;
        fNumberGenerator: INumberGenerator;
        procedure DoExportNumber;
      protected
        procedure Execute; override;
      public
        constructor Create(const list: TStrings; const numberGenerator: INumberGenerator);
      end;var
      Form13: TForm13;implementation{$R *.dfm}procedure TForm13.btn1Click(Sender: TObject);
    var
      i: integer;
      generator: INumberGenerator;
    begin
      generator := Self as INumberGenerator;
      for I := 1 to 5 do
        TNumExportThread.Create(lst1.Items, generator);
    end;procedure TForm13.btn2Click(Sender: TObject);
    var
      i: integer;
    begin
      // 最简单的方式, 不用线程 效率也是最高的.
      lst1.Items.BeginUpdate;
      try
        for i := 0 to 10000 - 1 do begin
          lst1.Items.Add(IntToStr(i));
        end;
      finally
        lst1.Items.EndUpdate;
      end;
    end;procedure TForm13.btn3Click(Sender: TObject);
    begin
      ShowMessage(IntToStr(lst1.Items.Count));
    end;function TForm13.GenerateNextNumber: Integer;
    begin
      inc(fNumber);
      Result := fNumber;
    end;procedure TForm13.FormCreate(Sender: TObject);
    begin
      fNumber := -1;
    end;
    { TNumExportThread }constructor TNumExportThread.Create(const list: TStrings; const numberGenerator: INumberGenerator);
    begin
      fList := list;
      fNumberGenerator := numberGenerator;
      FreeOnTerminate := true;
      inherited Create(False);
    end;procedure TNumExportThread.DoExportNumber;
    var
      i : Integer;
    begin
      i := fNumberGenerator.GenerateNextNumber;
      if not fNumberGenerator.Finished then begin
        fList.Add(IntToStr(i)) ;
      end;
    end;procedure TNumExportThread.Execute;
    begin
      while not fNumberGenerator.Finished do begin
        Synchronize(DoExportNumber);
      end;
    end;
    function TForm13.Finished: Boolean;
    begin
      Result := fNumber > 10000;
    end;initialization
      ReportMemoryLeaksOnShutdown := true;
    end.
    值得注意是线程一般做Synchronize应该保证代码最小化, 也就是Synchronize的代码要最少, 例如只操作Vcl的部分, 上面的代码为什么还含有获取数字的代码部分呢, 那是因为为了保证5个线程是按1-10000的顺序输出, 而不是乱序的.
      

  14.   

    我的要求是动态创建5个线程,让这5个线程写入1-100000的值
    而不是创建5个线程分别向listbox内写入1-100000的值.
    ----------------------------
    我语文水平差, 看了几次都没看明白你到底要个什么东西
      

  15.   

    嘿嘿.不光是你,我写的我看了几遍自己还有些迷糊呢.
    意思就是创建的5个线程就在listbox内显示1-10000的数值.
    而不是创建5个线程,让listbox内显示5次1-10000的数值
      

  16.   

    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;type
      TForm1 = class(TForm)
        lst1: TListBox;
        btn1: TButton;
        procedure btn1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;  TDemoThread=class(TThread)
      private
        FiCounter:Integer;
        FiResult:Integer;
        procedure Disaply();
      protected
        procedure Execute();override;
      public
        constructor Create(var Counter:Integer);
      end;var
      Form1: TForm1;implementation{$R *.dfm}
    const
      COUNTER_MAX:Integer=1000;
    var
      iCounter :Integer;{ TDemoThread }constructor TDemoThread.Create(var Counter: Integer);
    begin
      FiCounter:=Counter;
      inherited Create(False);
      FreeOnTerminate:=True;
    end;procedure TDemoThread.Disaply;
    begin
      Form1.lst1.Items.Add( IntToStr(FiResult));
    end;procedure TDemoThread.Execute;
    begin
      while not terminated do
      begin
        FiResult:= InterlockedIncrement(iCounter);
        if FiResult<=COUNTER_MAX then
          Synchronize(Disaply)
        else
          Break;  end;
    end;procedure TForm1.btn1Click(Sender: TObject);
    var
      i:Integer;
    begin
      for i:=0 to 4 do
      begin
        TDemoThread.Create(iCounter);
      end;
    end;end.
      

  17.   

    我辛苦给你写个对的, 你给的分这么少, 人家写的有问题的反而那么多, 唉, 以后不会再这么热心了....., 记住了多线程是一个极易让人进入误区的, 还需要你多理解, 多测试, 你还没遇到多核, 还有CPU乱序等问题呢, 这些都是会影响很大的东西, 慢慢就知道了