2010-06-27 3 views
6

작업을 수행하기 위해 여러 스레드를 실행해야하는 콘솔 응용 프로그램을 만들고 있습니다. 내 문제는 스레드가 차례 차례로 실행되고있는 것보다 하나씩 (thread1의 start -> work -> end 그리고 오직 thread2를 시작 함) 실행된다는 것입니다. 또한 나는 같은 시간 (성능 문제)에서 작동하도록 10 개 이상의 스레드를 원하지 않습니다. Bellow는 콘솔 응용 프로그램과 사용 된 데이터 모듈의 예제 코드입니다. 내 응용 프로그램도 같은 방식으로 작동합니다. 쓰레드가 끝나면 그 정보로 데이터베이스를 채워야하기 때문에 데이터 모듈을 사용했습니다. 또한 뭔가를하기위한 이유 인 설명을위한 코드에 주석이 있습니다.왜이 콘솔 응용 프로그램에서 스레드가 순차적으로 실행됩니까?

응용 프로그램 콘솔 코드 :

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. 

그래서, 내가 문제가 내 스레드가 다른 후 하나를 실행하고, 대신 모든 작업하는 것입니다 말했듯이 데이터 모듈 코드 같은 시간. 또한 때로는 첫 번째 스레드 만 작동한다는 것을 알았습니다. 나머지는 모두 만들고 완료했습니다. 내 응용 프로그램에서 모든 코드는 try-excepts로 보호되지만 오류는 발생하지 않습니다.

누군가가 조언을 해줄 수 있습니까?

+3

* 세마포어 *를 사용하면 스레드를 한 번에 10 개 이상 실행할 수 있습니다. 스레드를 생성하기 전에 그것을 획득하고 각 스레드가 종료시이를 해제하게하십시오. 이미 10 개의 쓰레드가 실행 중이라면, 세마포어를 얻으려는 11 번째 시도는 다른 쓰레드가 종료 할 때까지 블록 될 것이다. 그런 다음 ProcessMessages busy-wait 루프가 필요하지 않습니다. 이러한 루프는 아무 것도하지 않고 애플리케이션이 결코 메시지를 보내지 않으므로 CPU 시간을 소비하므로 결코 처리 할 수있는 것이 없습니다. –

+0

당신이 취할 수있는 또 다른 단계는 실제로 동기화 할 대상이있을 때만'CheckSynchronize'를 호출하는 것입니다. 세마포어를 설정하고, 세마포어 핸들과 전역'SyncEvent' 변수에서 MsgWaitForMultipleObjects를 호출하십시오. Classes.pas에서 읽어보십시오. 세마포어가 신호를 받으면 새 스레드를 만듭니다. 이벤트가 발생하면 CheckSynchronize를 호출하십시오. –

+0

롭, 정보를 제공해 주셔서 감사합니다. 나는 세마포어를 읽었지만 어떻게 구현해야하는지 완전히 이해하고 있는지 확신 할 수 없다. 내가 뭘 읽었는지 WaitForSingleObject (무한, 처리)를 사용해야합니다 - 전체주기를 완료하는 스레드에 대한,하지만 어떻게 다른 스레드를 시작할 때 알 수 있습니까? 나는 10 개의 쓰레드로 세마포어를 만들어야한다고 생각하기 때문에 몇 가지 예제를 보았습니다.하지만 새 쓰레드를 만드는 방법을 줄이고 세마포어로 점심을 줄이는 방법은? 내 예제를 기반으로 약간의 예제를 제공 할 수 있습니까? 미리 감사드립니다. – RBA

답변

6

최소한 당신은 메인 루프의

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

외부를 넣어해야합니다. 이 대기 루프가 보류를 일으키는 원인입니다. 메인 루프의 모든 정수 i에 대해 FThreadCount가 0이 될 때까지 대기합니다.

사이드 호 : 일반적으로 중요한 섹션으로 로컬 변수를 보호 할 필요가 없습니다. 비록 거기에 프로세스 메시지가 있어도 재진입을 유발할 수 있으므로 문제가 발생할 수 있습니다.

-1

나는 필요한 것을 정확히하는 장치를 가지고 있습니다. 그냥에서 다운로드 :

Cromis.Threading

당신이 두 개의 클래스가 내부의

  1. TTaskPool : 작업의 풀. 비동기 작업을 수행하는 쉬운 방법.
  2. TTaskQueue : 비동기 작업 큐입니다. 표준 FIFO 대기열처럼 작동합니다.

TTaskQueue는 일반 바닐라 스레드와 함께 독립 실행 형으로 사용할 수 있습니다. 그것은 하나의 스레드 내에서 블록하고 요청을 대기열에 넣습니다.

이 아닌 경우 충분히 당신의 OmniThreadLibrary을 확인할 수 있습니다 : 이것은 내가 무엇을 훨씬 우수 강력한 스레딩 라이브러리입니다

OmniThreadLibrary

. 또한 사용하기가 더 복잡합니다 (그러나 여전히 고전적인 스레딩과 비교하여 매우 쉽습니다).

+0

나는 스레드가 하나씩 실행되도록하고 싶지 않다고 생각합니다. 그는 왜 그들이 평행하게 달리지 않는지 확신하지 못합니다. –

+0

어,이 책을 아주 잘 읽었습니다. 감사합니다 브루스 – Runner

1

나는 Marjan의 제안을 따라했으며 다음 코드가 올바르게 작동하는 것 같습니다. 나는 다른 사람들이 분석 할 수있는 응답 코드를 제공하기 위해 내 자신의 질문에 대답하고 필요한 경우 수정했다.

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이이 문제에 대한 세마포어 구현 방법에 대한 간단한 예를 들어 답장하면 여기에 전체 코드도 게시 할 것입니다.

+0

델파이로 세마포어 구현에 대한 기사와 예제가 있습니다 : http://edn.embarcadero.com/article/29908 – Mick