DLL/ActiveForm中的线程同步处理
经常看到有网友在论坛上问DLL或ActiveForm中线程同步的问题,说线程Synchronize同步执行代码时程序死掉没反应。
如大富翁论坛网友 一少 提出问题:
Dll窗口中创建线程的问题: 为何线程结束后OnTerminate指向的过程不执行?
http://www.delphibbs.com/delphibbs/dispq.asp?LID=2801313
大富翁论坛网友 a_abj 提出问题:
我在 activexForm(写了个ocx控件)里 使用了 thread的 Synchronize方法
无法返回(到这步就停哪了),如果是exe文件(普通form)就没问题
http://www.delphibbs.com/delphibbs/dispq.asp?lid=2724466
本文试着从分析Synchronize同步执行的实现机制入手,来解决DLL/ActiveForm中线程同步的问题。
线程中进行同步时调用的Synchronize函数,仅仅是把调用调用线程、调用方法地址、异常对象封装在一个同步结构中,然后调用处理同步结构的类方法Synchronize。
procedure TThread.Synchronize(Method: TThreadMethod);
begin
FSynchronize.FThread := Self;
FSynchronize.FSynchronizeException := nil;
FSynchronize.FMethod := Method;
Synchronize(@FSynchronize);
end;
class procedure TThread.Synchronize(ASyncRec: PSynchronizeRecord);
var
SyncProc: TSyncProc;
begin
if GetCurrentThreadID = MainThreadID then // 如果是主线程,当然也就不需要同步处理了,直接执行即可
ASyncRec.FMethod
else
begin
SyncProc.Signal := CreateEvent(nil, True, False, nil); // 创建一个未命名事件,用于主线程执行同步的过程结束时通知等待线程
try
EnterCriticalSection(ThreadLock); // 进入ThreadLock关键区,用于保护对Classes单元的全局变量SyncList的访问和进行一些调用的同步
try
if SyncList = nil then
SyncList := TList.Create;
SyncProc.SyncRec := ASyncRec;
SyncList.Add(@SyncProc); // 把要同步的结构添加到列表SyncList中,等待主线程取出执行同步过程代码
SignalSyncEvent; // 调用SetEvent(SyncEvent)使同步事件SyncEvent处于信号状态,主要用在正确处理线程结束时
if Assigned(WakeMainThread) then
WakeMainThread(SyncProc.SyncRec.FThread); // 关键!Classes.WakeMainThread就是Application.WakeMainThread,通过PostMessage发个WM_NULL空消息,让主线程醒来
LeaveCriticalSection(ThreadLock); // 离开关键区,因为主线程调用的过程CheckSynchronize中会在取出SyncList列表同步结构前先进入关键区的
try
WaitForSingleObject(SyncProc.Signal, INFINITE); // 等待主线程同步调用结束通知事件
finally
EnterCriticalSection(ThreadLock);
end;
finally
LeaveCriticalSection(ThreadLock);
end;
finally
CloseHandle(SyncProc.Signal);
end;
if Assigned(ASyncRec.FSynchronizeException) then raise ASyncRec.FSynchronizeException;
end;
end;
在主线程的消息处理过程WndProc中检索到WM_NULL消息就会调用CheckSynchronize对同步列表SyncList中的同步结构进行逐个处理,直至SyncList中要同步的方法全部被同步调用完毕。而在应用程序空闲时也会调用CheckSynchronize进行同步处理。
procedure TApplication.WndProc(var Message: TMessage);
begin
try
(略)
with Message do
case Msg of
WM_NULL:
CheckSynchronize;
else
Default;
end;
except
HandleException(Self);
end;
end;
procedure TApplication.Idle(const Msg: TMsg);
begin
(略)
if (GetCurrentThreadID = MainThreadID) and CheckSynchronize then
Done := False;
if Done then WaitMessage;
end;
再看一下Delphi封装的实际线程执行体函数:
function ThreadProc(Thread: TThread): Integer;
var
FreeThread: Boolean;
begin
try
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
FreeThread := Thread.FFreeOnTerminate; // 是否线程执行结束后自动释放Thread对象实例?
Result := Thread.FReturnValue;
Thread.DoTerminate; // 这里调用Synchronize(CallOnTerminate),也就是说OnTerminate事件代码实际上是在主线程中同步执行的
Thread.FFinished := True;
SignalSyncEvent; // 使同步事件SyncEvent处于信号状态,使线程Destory调用WaitFor时如果是主线程,可以进行必要的同步处理
if FreeThread then Thread.Free; // 注意:线程Destroy部分的代码是在线程执行体中执行的
EndThread(Result);
end;
end;
到这里我们基本清楚了Synchronize同步的实现机制:
线程将同步线程、方法封装成同步结构,添加到同步列表SyncList中,然后发送WM_NULL消息唤醒主线程,然后调用WaitForSingleObject挂起等待主线程处理。主线程处理WM_NULL消息或在空闲时调用CheckSynchronize,从同步列表SyncList取出同步结构,在主线程中调用执行同步方法,执行完毕通过同步结构中的未命名事件句柄通知等待线程。等待线程收到事件通知后醒来,然后继续执行。
以上是对通常的应用程序中的线程Synchronize同步实现机制的分析,而对于DLL中的线程在用Synchronize进行同步时又有它的特殊性。
DLL中的全局变量是每个DLL都复制一份的,各个DLL之间以及DLL与主程序之间不能直接进行数据共享。这也就是说主程序的SyncEvent、SyncList、ThreadLock与DLL中的SyncEvent、SyncList、ThreadLock变量是不一样的。因此在DLL中的Synchronize同步,使用到的是DLL中的SyncEvent、SyncList、ThreadLock等变量,因此,直接使用Synchronize,主程序的CheckSynchronize就无法对DLL中的线程进行同步调度执行。而DLL中的Application却从来没有运行Run进入消息循环,因此也不能调用CheckSynchronize来处理线程的同步。因此在DLL中就需要主动调用CheckSynchronize函数来对同步列表SyncList进行处理。可以在DLL中专门创建一个窗口,在窗口消息循环中处理WM_NULL消息,调用ChechSynchronize即可。
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do
if Msg.message = WM_NULL then
CheckSynchronize
else
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
对ActiveForm中使用的线程调用Synchronize的情况也大体上相同,它的宿主进程是浏览器(如IE),IE一来不是Delphi编的程序,不会在主程序消息循环主调用CheckSynchronize进行同步处理的;二来即使它进行同步处理,也不是采用Delphi的同步实现方法,没有SyncList、SyncEvent、ThreadLock等变量的使用的;另外,ActiveForm由于是个OCX(实际上是种DLL),因此还有如同上述DLL中线程同步的问题,解决办法也可以和上面一样。当然,应该也可以直接在ActiveForm中添加处理WM_NULL消息的处理过程,调用CheckSynchronize进行同步处理,我没有进行试验,具体地,你可以亲自试一下。
另可参考:
Delphi 6, 7 threads synchronization in ActiveX controls
摘要: This article describes how to improve Delphi 6, 7 threads synchronization in ActiveX controls
http://dn.codegear.com/cn/article/32756
分享到:
相关推荐
本文将深入探讨 `Synchronize` 的工作原理,并提出解决 DLL/ActiveForm 中线程同步问题的策略。 `Synchronize` 的实现基于事件和线程同步原语。当一个非主线程调用 `Synchronize` 时,它会创建一个同步记录结构,...
6. **兼容性问题**:由于ActiveForm依赖于ActiveX,这限制了它的使用范围,因为大多数现代浏览器(尤其是非IE浏览器)不再支持ActiveX,这可能会导致在其他浏览器上的兼容性问题。 学习和理解ActiveForm的例子可以...
### Delphi2010与Delphi XE3中创建及发布ActiveForm的详细方法 #### 一、创建ActiveForm 在Delphi2010和Delphi XE3中创建ActiveForm涉及到以下步骤: ##### 1. 创建ActiveLibrary工程 - **第一步:** 打开Delphi...
MDIClient->ActiveForm = subForm; } FreeLibrary(dllHandle); } ``` 为了实现更复杂的交互,可以在DLL中定义更多的接口函数,比如传递数据、处理事件等。MDI子窗体可以作为独立的组件,拥有自己的生命周期管理...
6. **多线程编程**:为了实现异步操作,如接收新消息时保持用户界面的响应性,可能会使用多线程技术。 在www.pudn.com.txt和zZ这两个文件中,可能包含了项目的源代码、文档、示例或其他相关资源。如果要深入学习或...
在这个环境中,ActiveForm是一种技术,它允许开发者将Visual Component Library (VCL)组件封装到一个ActiveX控件中,使得这些控件可以在其他支持ActiveX的环境中如Visual Basic、HTML页面或者其他的C++ Builder项目...
- **兼容性问题**:在Delphi XE中创建ActiveForm时,需要特别注意兼容性问题,特别是当目标应用程序为旧版本时。 - **安全性考虑**:由于ActiveX控件具有较高权限,因此在设计时应充分考虑到安全性和潜在的风险。 - ...
【Delphi ActiveForm 实例】 在Delphi编程环境中,ActiveForm是一种技术,允许开发者将桌面应用程序的界面组件和逻辑转换为可以在Web环境中运行的组件。这个技术是Delphi对Web应用程序开发的一种扩展,使得传统的...
在实际开发中,ActiveForm技术可能涉及到的安全问题和兼容性问题也是需要注意的。例如,由于ActiveX的流行度已经下降,现代浏览器往往不支持或者限制ActiveX控件的运行,因此在开发时需要考虑到跨平台和多浏览器兼容...
自己制作ActiveForm电子签名的演示文档,具体步骤察看压缩包的Word文档。 先要有工具包,包括以下几个软件: makecert.exe 制作cer格式的证书,即X.509证书,同时可以创建私钥 cert2spc.exe 将cer格式证书转换成...
在Yii2中,ActiveForm是一个非常有用的组件,它帮助开发者快速创建表单,并且能够提供自动的客户端验证以及优雅的表单布局。 ### 表单的生成 在Yii2中创建表单需要使用ActiveForm组件,通常以开始标签和结束标签...
本示例聚焦于“ActiveForm”,它是Delphi中一个重要的概念,用于创建动态的、交互性强的用户界面。在Delphi中,ActiveForm是VCL(Visual Component Library)框架的一部分,它允许开发者将窗体(Forms)作为组件...
在本示例中,我们关注的是"ActiveForm编程",这涉及到创建ActiveX控件,这是一种可以在不同应用程序间共享的组件。ActiveX Form是特定类型的ActiveX控件,它是一个用户界面元素,可以在多种支持ActiveX技术的环境中...
本文将详细讲解ActiveForm的各种组件用法,并通过实例展示如何使用它们。 首先,让我们来看一下如何初始化一个ActiveForm。在PHP代码中,我们使用`ActiveForm::begin()`方法来开始一个表单,指定表单的属性如`...
php $form = ActiveForm::begin([ 'action' => ['save'], //提交地址(*可省略*) 'method'=>'post', //提交方法(*可省略默认POST*) 'id' => 'form-save', //设置ID属性 'options' => [ 'class' => 'form-...
前面我们讲述了yii2中如何使用modal以及yii2 gridview列表内更新操作如何使用modal的问题,本以为modal要告一段落可以开始新的话题了,但是实际问题往往超乎想像,这不modal弹窗提交的表单说是怎么验证的问题又出来...