首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当一个控件被销毁时,我应该何时停止我的线程(TCustomControl后代)

当一个控件被销毁时,我应该何时停止我的线程(TCustomControl后代)
EN

Stack Overflow用户
提问于 2012-12-27 16:20:58
回答 3查看 230关注 0票数 2

我的TCustomControl后代使用线程,这涉及到使用InvalidateRect使其无效。我遇到过这样的情况,当我在线程工作时关闭程序时,我不会停止销毁中的线程,因为甚至在我进入组件销毁的第一行之前(我通常会发出信号并等待线程停止工作),线程中的代码会使程序显示异常“Control... has no parent window”可能是在句柄请求上。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-12-27 17:13:54

所有的窗口交互--比如对InvalidateRect的调用--都应该在主线程中完成,所以把它从支持线程同步到主线程,或者去掉额外的线程。

其次,当一个控件被销毁时,你不能再使用它的窗口句柄了,因为它可能已经消失了。在这种情况下,VCL将再次尝试重新创建句柄,尽管控件正在被销毁,从而导致各种错误。如果您显然或必须具有在控件销毁期间可能调用的绘制指令,则在该代码周围进行此检查:

代码语言:javascript
运行
复制
if not (csDestroying in ComponentState) then

当然,这段代码也必须在主线程中!(不是因为窗口句柄已经被销毁,而是因为VCL不是线程安全的)。这是隐含的,因为后面的代码-绘画-现在也在主线程中。

票数 4
EN

Stack Overflow用户

发布于 2012-12-27 16:28:20

这是从线程访问TWinControl.Handle属性时的标准错误-在线程的上下文中重新创建底层窗口。

严格地说,从线程访问TWinControl.Handle是不安全的,尽管这个问题通常会出现在某些特殊情况下,比如关闭应用程序。

TWinControl类还提供了受保护的WindowHandle属性。您可以在不强制创建窗口句柄的情况下读取值。如果从TWinControl (或TCustomControl)派生的组件提供对它的访问,则可以从线程访问它。

票数 1
EN

Stack Overflow用户

发布于 2012-12-28 04:29:40

公认的答案仍然是有意义的,但我只是提供了一个对多线程和VCL (TVCLMutex)有用的对象,所以它在一定程度上回答了我自己的问题。诀窍是我在主线程中创建了另一个窗口,以便它共享相同的消息队列。从那时起,在自己的消息中等待使VCL变得神奇地安全,至少在大多数情况下是如此(因为所有代码都直接或间接地来自同一队列)。因此,如果您的线程想要访问/更改VCL中的某些内容,请创建一个TVCLMutex实例,并将不安全的访问代码包装在它的访问/释放中,这样您就可以确保主线程在windows核心中的某个位置等待继续处理消息,而不是在其他地方。

代码语言:javascript
运行
复制
unit VclMutex;

interface uses Windows, Messages, SysUtils;

const
  WM_WaitInMainThread = WM_USER + 1;

type
  TVCLMutex = class
  private
    fVCLWaitEvent: THandle;
    fAccessAvailEvent: THandle;
    fWnd: HWnd;
    fThreadId: Cardinal;
    fLevel: integer;
  protected
    procedure   WndProc(var Msg: TMessage);
  public
    constructor Create;
    destructor  Destroy;override;
    procedure   Access;
    procedure   Release;
  end;

implementation uses Forms;

{ TVCLMutex }

constructor TVCLMutex.Create;
begin
  inherited Create;

  if GetCurrentThreadId <> MainThreadId then
    raise Exception.Create('The object should be created in the main thread');

  fWnd:=AllocateHWnd(WndProc);
  fVCLWaitEvent:=CreateEvent(Nil, false, false, Nil);
  fAccessAvailEvent:=CreateEvent(Nil, false, true, Nil); 
end;

destructor TVCLMutex.Destroy;
begin
  CloseHandle(fVCLWaitEvent);   
  CloseHandle(fAccessAvailEvent);

  DeallocateHWnd(fWnd);
  inherited;
end;

procedure TVCLMutex.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_WaitInMainThread:
      begin
           { ReplyMessage make return from SendMessage call called from other thread }
        ReplyMessage(0);

           { Waiting to release VCL (main thread) queure. Signal will come from the
             Release method of working thread  }
        WaitForSingleObject(fVCLWaitEvent, INFINITE);
      end
    else
      Msg.Result := DefWindowProc(FWnd, Msg.Msg, Msg.wParam, Msg.lParam);
  end;
end;

procedure TVCLMutex.Access;
var
  AThreadId: Cardinal;
begin
  AThreadId:=GetCurrentThreadId();

  if GetCurrentThreadId = fThreadId then
  begin
      { If owning thread then allow while keeping nested level }
    Inc(fLevel);
  end
  else
  begin
       { Waiting for section to be available (from other threads) }
    WaitForSingleObject(fAccessAvailEvent, INFINITE);

       { Owning thread }
    fThreadId:=AThreadId;

       { Ask main thread queue to wait }
    if fThreadId <> MainThreadId then
      SendMessage(fWnd, WM_WaitInMainThread, 0, 0);

    fLevel:=1;
  end
end;

procedure TVCLMutex.Release;
begin
  Dec(fLevel);

  if fLevel = 0 then { All nested releases passed  }
  begin
        { Signaling for queue to release (if different thread) }
    if fThreadId <> MainThreadId then
      SetEvent(fVCLWaitEvent);

    fThreadId:=0;
       { Signaling that the mutex section is available }
    SetEvent(fAccessAvailEvent);
  end;
end;

end.
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14051544

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档