本文试着从分析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进行同步处理,我没有进行试验,具体地,你可以亲自试一下。

参考:http://m.blog.csdn.net/blog/fghydx/18699709

DLL ActiveForm 线程同步问题的更多相关文章

  1. 第9章 用内核对象进行线程同步(4)_死锁(DeadLock)及其他

    9.7 线程同步对象速查表 对象 何时处于未触发状态 何时处于触发状态 成功等待的副作用 进程 进程仍在运行的时候 进程终止的时(ExitProcess.TerminateProcess) 没有 线程 ...

  2. C#线程同步(2)- 临界区&Monitor

    文章原始出处 http://xxinside.blogbus.com/logs/46740731.html 预备知识:C#线程同步(1)- 临界区&Lock 监视器(Monitor)的概念 可 ...

  3. [delphi]在DLL中多线程同步Synchronize卡死问题

    在dll中多线程同步调用Synchronize不可以,会出现假死卡住的现象.可通过Sendmessage实现. 转网上其他文章解释: Application.Initialize; begin     ...

  4. MFC——9.多线程与线程同步

    Lesson9:多线程与线程同步 程序.进程和线程是操作系统的重点,在计算机编程中.多线程技术是提高程序性能的重要手段. 本文主要解说操作系统中程序.进程和线程之间的关系,并通过相互排斥对象和事件对象 ...

  5. .NET中的异步操作及线程同步

    执行异步操作 CLR使用了WIN的线程处理能力,但保留了与其分离的权利.某些时候CLR的线程与Win的线程不是完全的匹配. 线程的系统开销较大,应限制其数量. 创建:分配并初始化一线程内核对象,保留1 ...

  6. [ 高并发]Java高并发编程系列第二篇--线程同步

    高并发,听起来高大上的一个词汇,在身处于互联网潮的社会大趋势下,高并发赋予了更多的传奇色彩.首先,我们可以看到很多招聘中,会提到有高并发项目者优先.高并发,意味着,你的前雇主,有很大的业务层面的需求, ...

  7. C#多线程之线程同步篇3

    在上一篇C#多线程之线程同步篇2中,我们主要学习了AutoResetEvent构造.ManualResetEventSlim构造和CountdownEvent构造,在这一篇中,我们将学习Barrier ...

  8. C#多线程之线程同步篇2

    在上一篇C#多线程之线程同步篇1中,我们主要学习了执行基本的原子操作.使用Mutex构造以及SemaphoreSlim构造,在这一篇中我们主要学习如何使用AutoResetEvent构造.Manual ...

  9. C#多线程之线程同步篇1

    在多线程(线程同步)中,我们将学习多线程中操作共享资源的技术,学习到的知识点如下所示: 执行基本的原子操作 使用Mutex构造 使用SemaphoreSlim构造 使用AutoResetEvent构造 ...

随机推荐

  1. OKHttp的简单使用

    一方面,最近关于OKHttp的讨论甚嚣尘上,另一方面,我最近也更新了android6.0,发现在6.0中HttpClient不能使用了,于是决定抽时间也看一下OKHttp,总结了一点东西,与大家分享. ...

  2. Java基础知识强化之IO流笔记25:FileInputStream / FileOutputStream 复制图片案例

    1.  需求:把D:\\美女.jpg 复制到当前项目目录下mn.jpg 代码示例: package com.himi.filecopy; import java.io.FileInputStream; ...

  3. RedHat7上安装PHP

    编译安装PHP 下载PHP# wget http://cn2.php.net/distributions/php-7.0.0.tar.gz 解压缩PHP# tar -zxvf php-7.0.0.ta ...

  4. 文件权限和目录权限详解(rwx)

    [文件] r:可读,可以使用cat命令查看文件内容: w:可写,可以编辑或删除文件: x:可执行,可以当作命令提交给内核 [目录] r:可以对此目录执行ls,列出内部所有文件 w:可以在此目录创建文件 ...

  5. PL/SQL Developer 远程连接Oracle数据库

    PL/SQL Developer 远程连接Oracle数据库 网上搜了很多方法,这个可行! 1.    配置服务器tnsnames.ora文件,如果本机上没有安装oracle,可以从安装了oracle ...

  6. H TML5 之 (1) 初识HTML5

    新特性 HTML5 中的一些有趣的新特性: 用于绘画的 canvas 元素 用于媒介回放的 video 和 audio 元素 对本地离线存储的更好的支持 新的特殊内容元素,比如 article.foo ...

  7. Error parsing XML: not well-formed (invalid token)

    从网络上或别的文件复制粘贴进来的代码有隐含格式,可将内容先粘贴进记事本清除格式,再复制粘贴进工程文件,即可解决此问题 注:1. 要使工程文件全选清空, 2. 若粘贴后刷新仍无效果,可手动输入

  8. 第八章 CTE 递归 及 分组汇总 高级部分(多维数据集)

    UNION 等集合操作符: UNION 等以第一个 SELECT  的 列明 作为 整个结果集的列明,整个结果集 唯一认可的 唯一逻辑处理阶段 是 ORDER BY  这个意思是说 只有 ORDER ...

  9. This 在 C# 中的含义

    这涉及到c# 中的oo思想,其实不管在c# 或其他编码语言中,很多抽象的概念当你项目经验多了,自然而然就会对这些东西理解的更透彻点,更加具象. 这里有一些面向对象编程的概念需要说明:类(Class)的 ...

  10. iOS崩溃报告获取二

    // // JKExceptionHandler.h // JKExceptionHandler // // Created by Jack on 16/9/7. // Copyright © 201 ...