2012-02-05 2 views
1

내 응용 프로그램에서 스레드 기반 작업을 사용합니다. 그들은 잘 작동하지만 때로는 응용 프로그램을 걸어. 아래의 코드에서 procedure StopWaitFor 절차에 때때로 응답하지 않습니다. FStopEvent.SetEvent이 항상 작동하는 것으로 보이지 않기 때문입니다. 스레드가 Execute 절차 입사 정상 실행되는 동안왜 스레드가 waitfor에서 정지합니까?

후 다음 종료 후 처리를 수행하고, (Terminated 설정) Stop가 호출 될 때까지 OnWork 절차를 실행한다. 그것은 WaitFor에 대한 신호이며 모두가 행복합니다. 내 사용에서는이 작업이 파괴되기 때문에 발생합니다. 이 경우 Stop을 호출하는 기본 클래스의 소멸자가 호출됩니다.

경우에 따라 작동하지 않는 경우도 있습니다. Execute이 올바르게 입력되면 OnWork 프로 시저 호출은 ok로 실행되지만 FStopEvent.SetEvent에는 반응이 없습니다. 충돌이 발생하지 않았습니다 (except에있는 진술은 실행되지 않습니다). WaitFor가 반환되지 않기 때문에 프로그램이 중단됩니다. 디버그 DCU를 사용하면 프로그램이 WaitForSingleObject(H[0], INFINITE);에 멈추는 Classes 단위의 WaitFor으로 다시 추적 할 수 있습니다. OnWork 콜백은 동일합니다.

OnBeforeWork 및 OnAfterWork 프로시 저는 nil입니다. MaxLoops = -1FreeOnTerminate = False. 나는 절망적이다. 누군가가 빠져 나갈 수 있기를 바란다.

편집 1 :WaitFor 나는 아래 클래스 TEvent_Driven_Task에 대해 이야기하고 있습니다. 이 클래스는 클래스 TSimple_Task에서 파생되었으므로이 클래스를 추가하여 완성되었습니다.

편집 2 : 마르 얀 Venema가이 문제를 일으킬 수 있다는 언급으로Application.ProcessMessagesTSimple_Task.Stop에서 제거되었습니다. 결과는 동일합니다 (프로그램은 WaitFor에 있습니다).

unit Parallel_Event_Task; 

interface 

uses Forms, Windows, Classes, SysUtils, SyncObjs, 
    Parallel_Simple_Task; 

type 
    TEvent_Driven_Task = class (TSimple_Task) 
    private 
     FWorkEvent: TEvent; // Event signalling that some work should be done 

    public 
     constructor Create (work: TNotifyEvent; CreateSuspended: boolean = False; 
        max: Int32 = 1; 
        before: TNotifyEvent = nil; after: TNotifyEvent = nil; 
        terminate: boolean = True; task: integer = 1); override; 
     destructor Destroy; override; 
     procedure Activate (work: TNotifyEvent = nil); 
     procedure Execute; override; 
     procedure Stop; override; 
     procedure Release; override; 
    end; // Class: TEvent_Driven_Task // 

implementation 

constructor TEvent_Driven_Task.Create 
(
    work: TNotifyEvent;  // Work to do in Execute loop 
    CreateSuspended: boolean = False; // False = start now, True = use Start 
    max: Int32 = 1;   // Max loops of Execute loop, negative = infinite loop 
    before: TNotifyEvent = nil;// Called before Execute loop 
    after: TNotifyEvent = nil; // Called after Execute loop 
    terminate: boolean = True; // When true free the task on termination 
    task: integer = 1   // Task ID 
); 
begin 
    inherited Create (work, CreateSuspended, max, before, after, terminate, task); 

    FWorkEvent := TEvent.Create (nil, False, False, ''); 
end; // Create // 

Destructor TEvent_Driven_Task.Destroy; 
begin 
    inherited Destroy; 
end; // Destroy // 

procedure TEvent_Driven_Task.Activate (work: TNotifyEvent = nil); 
begin 
    if Assigned (work) then OnWork := work; 
    FWorkEvent.SetEvent; 
end; // Activate // 

// Execute calls event handler OnWork in a while loop. 
// Before the loop is entered, OnBeforeWork is executed, after: OnAfterWork. 
procedure TEvent_Driven_Task.Execute; 
var two: TWOHandleArray; 
    pwo: PWOHandleArray; 
    ret: DWORD; 
begin 
    pwo := @two; 
    pwo [0] := FWorkEvent.Handle; 
    pwo [1] := FStopEvent.Handle; 
    NameThreadForDebugging (AnsiString (FTaskName)); 
    FLoop := 0; 
    try 
     if Assigned (OnBeforeWork) then OnBeforeWork (Self); 
     while (not Terminated) and (Loop <> Max_Loops) do 
     begin 
     FLoop := FLoop + 1; 
     ret := WaitForMultipleObjects (2, pwo, FALSE, INFINITE); 
     if ret = WAIT_FAILED then Break; 
     case ret of 
      WAIT_OBJECT_0 + 0: if Assigned (OnWork) then OnWork (Self); 
      WAIT_OBJECT_0 + 1: Terminate; 
     end; // case 
     end; // while 
     if Assigned (OnAfterWork) then OnAfterWork (Self); 

// Intercept and ignore the interruption but keep the message 
    except 
     on e: exception do FError_Mess := e.Message; 
    end; // try..except 
end; // Execute // 

procedure TEvent_Driven_Task.Stop; 
begin 
    Terminate; 
    FStopEvent.SetEvent; 
    if not FreeOnTerminate 
     then WaitFor; 
end; // Stop // 

procedure TEvent_Driven_Task.Release; 
begin 
    inherited Release; 

    FWorkEvent.Free; 
end; // Release // 

end. // Unit: Parallel_Simple_Task // 

============= 기본 클래스 =======================

unit Parallel_Simple_Task; 

interface 

uses Windows, Classes, SysUtils, SyncObjs, Forms; 

type 
    TSimple_Task = class (TThread) 
    protected 
     FStopEvent: TEvent;   // Event signalling that the thread has to terminate, set by Stop 
     FTaskID: integer;    // Task sequence number 
     FTaskName: string;   // Task name 
     FLoop: integer;    // Indicates number of times Work has been processed 
     FMax_Loops: integer;   // Maximum # of iterations 
     FError_Mess: string;   // Error message if an exception occurred, else empty 
     FOnBeforeWork: TNotifyEvent; // Event to be called just before thread loop is entered 
     FOnWork:  TNotifyEvent; // Event caled in Execute loop 
     FOnAfterWork: TNotifyEvent; // Event to be called just after thread loop is finished 

     procedure set_name (value: string); 

    public 
     constructor Create (work: TNotifyEvent; CreateSuspended: boolean = False; max: Int32 = 1; 
        before: TNotifyEvent = nil; after: TNotifyEvent = nil; 
        terminate: boolean = True; task: integer = 1); reintroduce; virtual; 
     destructor Destroy; override; 
     procedure Execute; override; 
     procedure Stop; virtual; 
     procedure Release; virtual; 

     property TaskID: integer read FTaskID; 
     property TaskName: string read FTaskName write set_name; 
     property Loop: integer read FLoop; 
     property Max_Loops: integer read FMax_Loops write FMax_Loops; 
     property OnBeforeWork: TNotifyEvent read FOnBeforeWork write FOnBeforeWork; 
     property OnWork:  TNotifyEvent read FOnWork  write FOnWork; 
     property OnAfterWork: TNotifyEvent read FOnAfterWork write FOnAfterWork; 
    end; // Class: TSimple_Task // 

implementation 

constructor TSimple_Task.Create 
(
    work: TNotifyEvent;  // Work to do in Execute loop 
    CreateSuspended: boolean = False; // False = start now, True = use Start 
    max: Int32 = 1;   // Max loops of Execute loop 
    before: TNotifyEvent = nil;// Called before Execute loop 
    after: TNotifyEvent = nil; // Called after Execute loop 
    terminate: boolean = True; // When true free the task on termination 
    task: integer = 1   // Task ID 
); 
begin 
// The thread will only be started when this constructor ends. 
    inherited Create (CreateSuspended); 

    FStopEvent  := TEvent.Create (nil, True, False, ''); 
    FError_Mess  := ''; 
    FTaskID   := task; 
    FTaskName   := ''; 
    Max_Loops   := max; 
    OnBeforeWork  := before; 
    OnWork   := work; 
    OnAfterWork  := after; 
    FreeOnTerminate := terminate; 
end; // Create // 

destructor TSimple_Task.Destroy; 
begin 
    Stop; 
    Release; 

    inherited Destroy; 
end; // Destroy // 

// Execute calls event handler OnWork in a while loop. 
// Before the loop is entered, OnBeforeWork is executed, after: OnAfterWork. 
procedure TSimple_Task.Execute; 
var ret: DWORD; 
begin 
    try 
     NameThreadForDebugging (AnsiString (FTaskName)); 

     FLoop := 0; 
     if Assigned (OnBeforeWork) then OnBeforeWork (Self); 
     while (not Terminated) and (FLoop <> Max_Loops) do 
     begin 
     ret := WaitForSingleObject (FStopEvent.Handle, 0); 
     if ret = WAIT_OBJECT_0 then 
     begin 
      Terminate; 
     end else 
     begin 
      if Assigned (OnWork) then OnWork (Self); 
      FLoop := FLoop + 1; 
     end; // if 
     end; // while 
     if not Terminated and Assigned (OnAfterWork) then OnAfterWork (Self); 
// Intercept and ignore the interruption but keep the message 
    except 
     on e: exception do FError_Mess := e.Message; 
    end; // try..except 
end; // Execute // 

procedure TSimple_Task.Stop; 
begin 
    Terminate; 
    FStopEvent.SetEvent; 
    if not FreeOnTerminate 
     then WaitFor; 
end; // Stop // 

procedure TSimple_Task.Release; 
begin 
    FStopEvent.Free; 
end; // Release // 

procedure TSimple_Task.set_name (value: string); 
begin 
    FTaskName := value; 
end; // set_name // 

end. // Unit: Parallel_Simple_Task // 
+0

실행중인 Execute 메서드는 무엇입니까? 너는 두 명을 올렸다. 어떤거야? 차단 된 스레드는 무엇입니까? 디버거가 당신에게 무엇을 말합니까? 현대 Delphis의 스레드 창은 차단하고있는 내용을 알려줍니다. –

+0

TEvent_Driven_Task에있는 스레드입니다. – Arnold

+0

스레드 상태를 의미하는지 디버거가 알려주지 않습니다. 모든 스레드가 중지되고 모든 상태를 알 수 없으며 대기 체인이 없습니다. 이는 실행 중 상태이기도하지만 OnWork 루틴이 많은 실행 시간을 필요로하지 않기 때문에 발생할 수 있습니다. 이 스레드는 FWorkEvent가 트리거되기를 기다리고 있습니다. – Arnold

답변

6

TThread.WaitFor() 기본 스레드 객체가 OS 계층에서 종료 될 때 스레드 핸들 (TThread.Handle 속성)이 신호를받을 때까지 기다립니다. 이 신호는 TThreadExecute() 메서드가 종료 된 후 (그리고 TThread.DoTerminate()이 호출되고 종료 된 후) 자체적으로 Win32 API ExitThread() 함수를 호출 할 때 발생합니다. 루프를 멈추기 위해 FStopEvent 신호를 보낸 경우에도 Execute() 메서드가 올바르게 종료되지 않는 교착 상태가 발생하는 것처럼 들리는 것을 들으실 수 있습니다. 주어진 코드를 보면 WaitForMultipleObjects()이 찾고 있지 않은 오류 코드를 반환하고 있거나 OnWork 이벤트 처리기가 올바르게 종료되지 않았을 가능성이 높으므로 Execute()이 종료 될 수 있습니다.

당신이 지금까지 보여준 모든 것들은 당신 자신의 작업 수업의 정의이지만 당신은 그들이 당신의 프로젝트에서 어떻게 실제로 사용되는지를 보여주지 않았습니다. 나머지 작업 논리를 보여주고 사람들이 문제가 무엇인지 추측하지 못하게하십시오.

먼저 제안 할 것은 은 소멸자에서 Stop()으로 전화를 겁니다.그것은 거기에 속하지 않습니다. NEVER 여전히 실행중인 스레드를 파괴하십시오. 스레드를 먼저 중지하고 스레드가 종료되기 전에 스레드가 종료 될 때까지 기다립니다. TThread 자체가 실행되는 동안 자체의 충분한 문제가 파괴되면 추가 할 필요가 없습니다.

+0

당신의 말은 완벽하게 의미가 있습니다. 문제의 원인이되는 코드를 격리하는데 성공하지 못했습니까? 그리고 그것은 너무 많은 코드를 게시해야한다는 것을 의미합니다. 누군가가 저에게 오류를 지적하기를 바랍니다. 그럼 스레드를 멈추는 방법을 조언 해 주셨습니다. Marjan Venema와 당신은 Execute에서 실행중인 작업의 교착 상태에 대해 경고했습니다. 그것은 신중하게 면밀히 조사하고 테스트하며 아마도 코드를 복원하는 것을 의미합니다. 스레드 코드를 변경하는 것보다 어렵습니다. 이 점을 지적 해 주셔서 감사합니다. – Arnold

+0

이 답변은 검색 할 수있는 몇 가지 단서를 제공해 주었고 올바른 것으로 표시하기로 결정했습니다. 나는 그들을 파괴하기 전에 실을 멈춘다. 도움이되지 않았지만 더 나은 코드를 산출합니다. 이제 모든 OnWork 이벤트 핸들러를 조심스럽게 살펴보고 있습니다. – Arnold

관련 문제