现在的位置: 首页 > 综合 > 正文

FastMM内存管理器在使用多线程情况下需要注意的问题。

2013年08月01日 ⁄ 综合 ⁄ 共 2308字 ⁄ 字号 评论关闭

        FastMM内存管理器在使用多线程情况下需要注意的问题。

问题1:

  注:如果你在Delphi中,只是用TThread类创建线程,不会用到API函数CreateThread创建线程,哪么下面这篇文章你可以完全忽视。。  
  当然,如果你耐心的看完了下面这篇文章,你也会明白一些原来我们习惯使用,但FastMM或Delphi默认的内存管理器在背后默默的为你处理复杂的内存管理问题中的一些技术细节。
  FastMM内存管理器在内存管理方面的高效、稳定大家是有目共睹的,这个方面大家在网上随便一搜,就可以找到很多FastMM的性能测试报告,我就不多啰嗦了。
  如果你在使用FastMM的环境下,创建了多个线程(Worker或UI线程),这个又分为2种情况:
1.使用TThread类创建线程。这种方式创建的线程在FastMM环境下运行完全没有问题;
2.使用纯API函数CreateThread创建线程,这种情况下,例如不做特殊处理,程序会频繁的出现内存访问错误。
  现在就来讨论一下在使用FastMM的环境下使用纯API函数CreateThread创建线程,频繁出现内存访问错误的问题。
  问题的描述:因为我需要建立一个带自建消息循环的线程,以便使该线程能够响应其它线程发送的消息,所以我只有使用API函数CreateThread创建线程,并且在线程中使用了Tlist和String。这样,运行程序后,程序频繁的出现了内存访问错误。多线程情况下,问题就是复杂,开始我以为是Tlist和String数据线程同步的问题,当使用了临界区和互斥的情况下,仍然频繁发生内存访问错误。没办法,我只有将项目中use FastMM4去掉,使用Delphi默认的内存管理器,但是问题依旧出现,这时候我想起了exe和dll共享内存时,要加入ShareMem,我就试着加上了ShareMem,再运行程序,结果,程序运行了一天也没有发生任何错误。
  我当时就在想,虽然加入ShareMem,“解决”了上面这个问题,但ShareMem是为了解决exe和dll共享内存时产生的问题,而我里只是用了一个exe,并没有用到dll,这是怎么回事?查看ShareMem相关资料,发现一个重要的环境变量IsMultiThread,它指明系统是否运行在多线程环境下,如果该全局变量为True,哪么在多线程环境下,内存的分配和释放都要用“临界区”进行同步以保证安全。而我在线程中用到了String,它正是在线程中动态的分配和释放了内存,所以引起了冲突。
  知道了问题所在,在FastMM使用在多线程的环境下,FastMM管理器在线程中分配和释放内存的时候也是要判断环境变量IsMultiThread,也是要同步以保证安全。当然FastMM和Delphi默认的内存管理器在这点上使用的同步方法略有差异,一个是使用临界区同步,另一个是使用CPU指令锁定同步,当然对用户来说,结果是一样的,哪就是在多线程的环境下,内存的分配和释放一定要同步保护。
  好了,现在在使用FastMM时,只要手动将IsMultiThread设为True,上面的错误就不会发生了,事实也证明了这一点,重新改过后,使用FastMM在多线程环境下就再也没有出现问题了,效率还大幅提高(相对于Delphi默认的内存管理器)。
  本来,问题已经解决完了,但我这人偏偏是一个喜欢刨根问底的人,因为我想到了原来我在FastMM下使用TThread类创建线程,并没有手动设置IsMultiThread环境变量,但也一直没有出现问题,这是为什么?
  原来,TThread类内部在Create时调用了BeginThread()函数,而BeginThread()函数是这样实现的:
  New(P);
  P.Func := ThreadFunc;
  P.Parameter := Parameter;
  IsMultiThread := TRUE;
  Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P,
    CreationFlags, ThreadID);

    看见了吗?里面已经将IsMultiThread := TRUE; 所以这样就不会出现问题了。

  原来如此啊,隐藏的好深啊。。。。。

问题2:在线程中使用FastMM管理内存后的内存泄漏问题。
  本来,这种情况一般情况下不是FastMM问题,但情况总有可能出现二般的情况。比如,你在线程的Execute()中动态创建了几个对象,甚至可能创建了接口对象(IInterface或IUnknown),你也记着在线程结束的时候销毁这些对象了,但程序运行后,有时候会发生少量的内存泄漏问题,一般情况下是对象,接口或者字符串这些资源。当然,大多数情况下,这种问题不会发生,比如你的程序比较小,只有几千行代码,哪是不会有问题的,要是你的程序比较大,有个七八万或十几万行。哪么,不注意的话,FastMM在线程中的内存泄漏问题会经常出现,搞的人很郁闷。。
  其实,解决起来也很简单,在线程最后退出的的代码中在对象Free后,加入sleep(100)或者sleep(xxx)就可以解决这个问题了,sleep(xxx)中的xxx是视你的程序的实际情况增加的延时,我这里测试,加入50毫秒以上的延时,基本上就不会出现内存泄漏问题了。加入sleep(xxx)的作用主要是让FastMM有时间清理对象释放后占用的内存。就是这么简单,但这是我痛苦了一个星期后才懂的道理。。现在你看到了这篇文章,就不用像我哪样痛苦一个星期了。。。。。

抱歉!评论已关闭.