代码之家  ›  专栏  ›  技术社区  ›  RBA

为什么线程在这个控制台应用程序中连续运行?

  •  6
  • RBA  · 技术社区  · 15 年前

    我正在创建一个控制台应用程序,它需要运行几个线程才能完成一个任务。我的问题是线程一个接一个地运行(thread1 start->工作->结束并只启动线程2),而不是同时运行所有线程。另外,我不希望有超过10个线程同时工作(性能问题)。下面是控制台应用程序和所用数据模块的示例代码。我的申请也是这样。我使用了datamodule,因为线程完成后,我必须用这些信息填充数据库。代码中也有注释来解释做某事的原因。

        program Project2;
    
    {$APPTYPE CONSOLE}
    
    uses
      SysUtils,
      Unit1 in 'Unit1.pas' {DataModule1: TDataModule};
    
    var dm:TDataModule1;
    begin
       dm:=TDataModule1.Create(nil);
       try
         dm.execute;
       finally
        FreeAndNil(dm);
       end;
    end.
    

        unit Unit1;
    
    interface
    
    uses
      SysUtils, Classes, SyncObjs, Windows, Forms;
    
    var   FCritical: TRTLCriticalSection;//accessing the global variables  
    
    type
      TTestThread = class(TThread)
      protected
        procedure Execute;override;
      end;
      TDataModule1 = class(TDataModule)
        procedure DataModuleCreate(Sender: TObject);
        procedure DataModuleDestroy(Sender: TObject);
      private
        { Déclarations privées }
      public
    
        procedure execute;
        procedure CreateThread();
        procedure Onterminatethrd(Sender: TObject);
      end;
    
    var
      DataModule1       : TDataModule1;
      FthreadCount      : Integer;  //know how many threads are running
    
    
    implementation
    
    {$R *.dfm}
    
    { TTestThread }
    
    procedure TTestThread.Execute;
    var
      f                 : TextFile;
      i                 : integer;
    begin
      EnterCriticalSection(fcritical);
      AssignFile(f, 'd:\a' + inttostr(FthreadCount) + '.txt');
      LeaveCriticalSection(fcritical);
      Rewrite(f);
      try
        i := 0;
        while i <= 1000000 do // do some work...
          Inc(i);
        Writeln(f, 'done');
      finally
        CloseFile(f);
      end;
    end;
    
    { TDataModule1 }
    
    procedure TDataModule1.CreateThread;
    var
      aThrd             : TTestThread;
    begin
      aThrd := TTestThread.Create(True);
      aThrd.FreeOnTerminate := True;
      EnterCriticalSection(fcritical);
      Inc(FthreadCount);
      LeaveCriticalSection(fcritical);
      aThrd.OnTerminate:=Onterminatethrd;
      try
        aThrd.Resume;
      except
        FreeAndNil(aThrd);
      end;
    end;
    
    procedure TDataModule1.Onterminatethrd(Sender: TObject);
    begin
      EnterCriticalSection(fcritical);
      Dec(FthreadCount);
      LeaveCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.DataModuleCreate(Sender: TObject);
    begin
      InitializeCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.DataModuleDestroy(Sender: TObject);
    begin
      DeleteCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.execute;
    var
      i                 : integer;
    begin
      i := 0;
      while i < 1000 do
      begin
        while (FthreadCount = 10) do
          Application.ProcessMessages;//wait for an thread to finish. max threads at a //time =10
    
        CreateThread;
    
        EnterCriticalSection(fcritical);
        Inc(i);
        LeaveCriticalSection(fcritical);
    
        while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread
        begin
          Application.ProcessMessages;
          CheckSynchronize;
        end;
      end;
    end;
    
    end.
    

    有人能给我个建议吗?

    3 回复  |  直到 9 年前
        1
  •  7
  •   Marjan Venema    15 年前

    至少你应该

    while FthreadCount > 0 do //wait for all threads to finish in order to close the //main thread
    begin
      Application.ProcessMessages;
      CheckSynchronize;
    end;
    

    在主回路外。这个等待循环是导致阻塞的原因。对于mainloop的每个整数i,它都会等待FThreadCount降到零。

    旁注:通常情况下,不需要用关键部分来保护局部变量。尽管在其中包含进程消息可能会把事情搞砸,因为它可能会导致重新进入。

        2
  •  1
  •   RBA    9 年前

    unit Unit1;
    
    interface
    
    uses
      SysUtils, Classes, SyncObjs, Windows, Forms, Dialogs;
    
    var   FCritical: TRTLCriticalSection;  
    
    type
      TTestThread = class(TThread)
      protected
        procedure Execute;override;
      end;
      TDataModule1 = class(TDataModule)
        procedure DataModuleCreate(Sender: TObject);
        procedure DataModuleDestroy(Sender: TObject);
      private
        { Déclarations privées }
      public
    
        procedure execute;
        procedure CreateThread();
        procedure Onterminatethrd(Sender: TObject);
      end;
    
    var
      DataModule1       : TDataModule1;
      FthreadCount      : Integer;
    
    
    implementation
    
    {$R *.dfm}
    
    { TTestThread }
    
    procedure TTestThread.Execute;
    var
      f                 : TextFile;
      i                 : integer;
    
    begin
     AssignFile(f, 'd:\a\a' + inttostr(FthreadCount) + '.txt');
     if fileexists('d:\a\a' + inttostr(FthreadCount) + '.txt') then
      Append(f)
     else
      Rewrite(f);
       try
        i := 0;
        while i <= 1000000 do
          Inc(i);
      Writeln(f, 'done '+floattostr(self.Handle));
      finally
        CloseFile(f);
      end;
    end;
    
    { TDataModule1 }
    
    procedure TDataModule1.CreateThread;
    var
      aThrd             : TTestThread;
    begin
      aThrd := TTestThread.Create(True);
      aThrd.FreeOnTerminate := True;
      EnterCriticalSection(fcritical);
      Inc(FthreadCount);
      LeaveCriticalSection(fcritical);
      aThrd.OnTerminate:=Onterminatethrd;
      try
        aThrd.Resume;
      except
        FreeAndNil(aThrd);
      end;
    end;
    
    procedure TDataModule1.Onterminatethrd(Sender: TObject);
    begin
      EnterCriticalSection(fcritical);
        Dec(FthreadCount);
      LeaveCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.DataModuleCreate(Sender: TObject);
    begin
      InitializeCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.DataModuleDestroy(Sender: TObject);
    begin
      DeleteCriticalSection(fcritical);
    end;
    
    procedure TDataModule1.execute;
    var
      i                 : integer;
    begin
      i := 0;
     try
      while i < 1000 do
      begin
        while (FthreadCount = 10) do
         begin
          Application.ProcessMessages;
          CheckSynchronize
         end;
        CreateThread;
        Inc(i);
      end;
        while FthreadCount > 0 do
        begin
          Application.ProcessMessages;
          CheckSynchronize;
        end;
     except on e:Exception do
    //
     end;
    end;
    
    end.
    

    目前,我已经测试了几次这个代码,它似乎工作得很好。如果Rob用一个小例子来回答我如何在这个问题上实现信号量,我也会在这里发布整个代码。

        3
  •  -1
  •   Runner    15 年前

    我有一个单位,你需要什么就做什么。只需从以下位置下载:

    Cromis.Threading

    内部有两个类:

    1. TTaskPool:任务池。异步操作的简单方法。
    2. TTaskQueue:异步任务的队列。工作方式类似于标准的FIFO队列。

    OmniThreadLibrary

    这是一个强大的线程库,远远优于我所拥有的。但也更复杂的使用(但仍然非常容易相比,经典线程)。