Multithreading 为什么一根线有时会挂在等待?

Multithreading 为什么一根线有时会挂在等待?,multithreading,delphi,Multithreading,Delphi,在我的应用程序中,我使用基于线程的任务。它们工作正常,但有时会挂起应用程序。在下面的代码中,过程停止有时挂起在等待过程中。这是因为FStopEvent.SetEvent似乎并不总是起作用 在正常执行期间,线程进入Execute过程,执行OnWork过程,直到调用Stop(设置终止),然后执行一些后处理,然后退出。这是等待退出的信号,每个人都很高兴。在我的使用中,发生这种情况是因为任务被销毁。在这种情况下,调用基类的析构函数,它调用Stop 在某些情况下,这是行不通的Execute输入正确,执行O

在我的应用程序中,我使用基于线程的任务。它们工作正常,但有时会挂起应用程序。在下面的代码中,
过程停止
有时挂起在
等待
过程中。这是因为
FStopEvent.SetEvent
似乎并不总是起作用

在正常执行期间,线程进入
Execute
过程,执行
OnWork
过程,直到调用
Stop
(设置
终止
),然后执行一些后处理,然后退出。这是等待退出的信号,每个人都很高兴。在我的使用中,发生这种情况是因为任务被销毁。在这种情况下,调用基类的析构函数,它调用
Stop

在某些情况下,这是行不通的
Execute
输入正确,执行
OnWork
过程调用正常,但对
FStopEvent.SetEvent
没有反应。没有崩溃(除了未执行之外,
处的语句),只是什么都没有。由于WaitFor未返回,程序挂起。通过调试DCU,我可以追溯到单元
中的
WaitFor
,其中程序挂起在
WaitForSingleObject(H[0],无穷大)
OnWork
回调是相同的

工作前和工作后的程序均为零
MaxLoops=-1
FreeOnTerminate=False
。我很绝望,希望有人能找到出路

编辑1:我所说的
WaitFor
发生在下面列出的类
TEvent\u-Driven\u任务中。因为这个类是从class
TSimple_Task
派生的,所以为了完整性,我添加了这个类

编辑2:
应用程序。ProcessMessages
已从
TSimple_任务中删除。停止
,因为Marjan Venema指出这可能会导致问题。结果是相同的(程序挂起在
WaitFor

单元并行事件任务;
接口
使用窗体、窗口、类、SysUtils、SyncObjs、,
并行简单任务;
类型
TEvent\u驱动的任务=类(TSimple\u任务)
私有的
FWorkEvent:TEvent;//事件,表示应完成某些工作
公众的
构造函数Create(work:TNotifyEvent;CreateSuspended:boolean=False;
max:Int32=1;
之前:TNotifyEvent=nil;之后:TNotifyEvent=nil;
终止:布尔值=True;任务:整数=1);推翻
毁灭者毁灭;推翻
程序激活(工作:TNotifyEvent=nil);
程序执行;推翻
程序停止;推翻
程序发布;推翻
完类别:TEvent_驱动的_任务//
实施
构造函数TEvent_驱动的任务。创建
(
work:TNotifyEvent;//在执行循环中要做的工作
CreateSuspended:boolean=False;//False=start now,True=use start
max:Int32=1;//执行循环的最大循环数,负=无限循环
before:TNotifyEvent=nil;//在执行循环之前调用
after:TNotifyEvent=nil;//在执行循环之后调用
terminate:boolean=True;//如果为True,则在终止时释放任务
任务:整数=1//任务ID
);
开始
继承的创建(工作、创建挂起、最大、之前、之后、终止、任务);
FWorkEvent:=TEvent.Create(nil,False,False,”);
完创造//
析构函数TEvent_Driven_Task.Destroy;
开始
继承性破坏;
完毁灭//
程序TEvent_Driven_Task.Activate(工作:TNotifyEvent=nil);
开始
如果分配(工作),则在职:=工作;
FWorkEvent.SetEvent;
完激活//
//Execute调用while循环中的OnWork事件处理程序。
//在进入循环之前,执行OnBeforeWork,after:OnAfterWork。
程序TEvent_Driven_Task.Execute;
变量2:TWOHandleArray;
pwo:pwohandlearay;
雷特:德沃德;
开始
pwo:=@2;
pwo[0]:=FWorkEvent.Handle;
pwo[1]:=FStopEvent.Handle;
名称线程为debugging(AnsiString(FTaskName));
FLoop:=0;
尝试
如果分配(上班前),则上班前(自己);
而(未终止)和(循环最大循环)则
开始
FLoop:=FLoop+1;
ret:=WaitForMultipleObjects(2,pwo,FALSE,无限);
如果ret=WAIT_失败,则中断;
案例检索
等待对象0+0:如果已分配(工作),则为工作(自身);
等待对象0+1:终止;
完案例
完虽然
如果分配(在下班后),则在下班后(自己);
//拦截并忽略中断,但保留消息
除了
关于e:exception do FError_Mess:=e.Message;
完试试……除了
完执行//
程序TEvent_Driven_Task.Stop;
开始
终止
FStopEvent.SetEvent;
如果不是自由终止
然后等待;
完停止//
程序TEvent_驱动_Task.Release;
开始
遗传释放;
免费;
完释放//
完单元:并行任务//
================基类=======================

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 //
单元并行简单任务;
接口
使用Windows、类、SysUtils、SyncObjs和窗体;
类型
t简单任务=类(TThread)
受保护的
FStopEvent:TEvent;//表示线程必须终止的事件,由Stop设置
fta:整数;//任务序列号
FTaskName:string;//任务名称
FLoop:integer;//指示已处理工作的次数
FMax_循环:整数;//最大迭代次数
FError_Mess:string;//如果发生异常,则显示错误消息,否则为空
FOnBeforeWork:TNotifyEvent;//在进入线程循环之前调用的事件
FOnWork:TNotifyEvent;//事件在执行循环中被标度
后续工作:TNotifyEvent;//在t之后调用的事件
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 //