以前在写个别程序的时候老是喜欢使用纯API编程。 
在C++中无大问题,可是到了DELPHI中情况就不一样了。 
当你用 DELPHI写的多线程程序莫名其妙的内存错误,特别是字符串(string)操作; 
或者程序无故终止,又没有任何提示,你需要认真分析可能是你直接使用了CreateThread。

C++的linker可以自己设置运行库的形式,选择支持单线程还是多线程模式。 
DELPHI是自动判别的,那他是如何自动判别的呢,这就要看看他在System单元提供的函数BeginThread了。 听说在VC 中也不赞成直接使用没有保护的
CreateThread ,也要使用加了保护的_BeginThread。

{$IFDEF MSWINDOWS} 
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord; 
  ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord; 
  var ThreadId: LongWord): Integer; 
var 
  P: PThreadRec; 
begin 
  New(P); 
  P.Func := ThreadFunc; 
  P.Parameter := Parameter; 
  IsMultiThread := TRUE; 
  Result := CreateThread(SecurityAttributes, StackSize, @ThreadWrapper, P, 
    CreationFlags, ThreadID); 
end;

看见了“ IsMultiThread := TRUE;”这句了吗? 
找到他的定义,在全局变量里: 
IsMultiThread: Boolean;   { True if more than one thread }

再看看ThreadWrapper: 
{$IFDEF MSWINDOWS} 
function ThreadWrapper(Parameter: Pointer): Integer; stdcall; 
{$ELSE} 
function ThreadWrapper(Parameter: Pointer): Pointer; cdecl; 
{$ENDIF} 
asm 
{$IFDEF PC_MAPPED_EXCEPTIONS} 
        { Mark the top of the stack with a signature } 
        PUSH    UNWINDFI_TOPOFSTACK 
{$ENDIF} 
        CALL    _FpuInit 
        PUSH    EBP 
{$IFNDEF PC_MAPPED_EXCEPTIONS} 
        XOR     ECX,ECX 
        PUSH    offset _ExceptionHandler 
        MOV     EDX,FS:[ECX] 
        PUSH    EDX 
        MOV     FS:[ECX],ESP 
{$ENDIF} 
        MOV     EAX,Parameter 
        MOV     ECX,[EAX].TThreadRec.Parameter 
        MOV     EDX,[EAX].TThreadRec.Func 
        PUSH    ECX 
        PUSH    EDX 
        CALL    _FreeMem 
        POP     EDX 
        POP     EAX 
        CALL    EDX 
{$IFNDEF PC_MAPPED_EXCEPTIONS} 
        XOR     EDX,EDX 
        POP     ECX 
        MOV     FS:[EDX],ECX 
        POP     ECX 
{$ENDIF} 
        POP     EBP 
{$IFDEF PC_MAPPED_EXCEPTIONS} 
        { Ditch our TOS marker } 
        ADD     ESP, 4 
{$ENDIF} 
end;

这里DELPHI帮你设置了线程的 SEH 处理函数。 
在DELPHI里,我们应该使用BeginThread,丢掉CreateThread吧。

*****************************************************************************

博主:在实际应用中出现了问题

function GetGuiyue(ABuffer: PArrayByte): Boolean; stdcall; external 'Guiyue.dll';
我调用函数GetGuiyue时出现异常,

BeginThread(nil,0,@GetGuiyue,tempBuffer,0,ThreadID);

原因是BeginThread 访问函数,与stdcall 接口冲突。所以需要在调用时写一个引用函数

function ParseGuiyue(ABuffer: PArrayByte): Boolean;  
begin
  GetGuiyue(ABuffer);  //调用DLL 里的函数
  EndThread(0);          //函数结束关闭线程
end;

这样,你就可以放心使用 BeginThread了。

BeginThread(nil,0,@ParseGuiyue,tempBuffer,0,ThreadID);  //创建子线程处理解析

但是,也有人提出 BeginThread 使用不安全

把ParseGuiyue作为BeginThread的参数有两个问题 
  1.P参数无效(ParseGuiyue会从栈顶获取,而实际上在EAX中传递过来) 
  2.函数无法正确返回(ParseGuiyue把栈顶的返回地址当成P参数了,而取了下一个不确定的元素作为返回地址) 
所以在MyThreadFunc中加EndThread只是让线程在函数返回前结束执行,并不能解决第一个问题——而这可能会带来严重的错误,因为ParseGuiyue里P参数是一个指向代码段内存的地址(ThreadWrapper函数的执行体中某位置)。 
  另外看起来调用EndThread会造成BeginThread中分配的PThreadRec内存泄漏。

http://www.cnblogs.com/wxy8/archive/2011/06/21/2085661.html

DELPHI中千万别直接使用CreateThread ,建议使用BeginThread(在C++中无大问题,可是到了DELPHI中情况就不一样了)的更多相关文章

  1. DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)

    函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...

  2. 关于Net开发中一些SQLServer性能优化的建议

    一. ExecuteNonQuery和ExecuteScalar 对数据的更新不需要返回结果集,建议使用ExecuteNonQuery.由于不返回结果集可省掉网络数据传输.它仅仅返回受影响的行数.如果 ...

  3. Java异常(二) 《Effective Java》中关于异常处理的几条建议

    概要 本章是从<Effective Java>摘录整理出来的关于异常处理的几条建议.内容包括:第1条: 只针对不正常的情况才使用异常第2条: 对于可恢复的条件使用被检查的异常,对于程序错误 ...

  4. 编写高质量代码改善C#程序的157个建议——建议68:从System.Exception或其他常见的基本异常中派生异常

    建议68:从System.Exception或其他常见的基本异常中派生异常 微软建议:从System.Exception或其他常见基本异常之一派生异常.在Visual Studio中输入Excepti ...

  5. 黑马基础阶段测试题:定义一个int类型的数组,数组中元素为{5,7,3,9,4}。求出数组中的最小值,并判断最小值是否为偶数,如果是偶数则输出“最小值为偶数”,如果不是偶数则输出“最小值为奇数”。打印如下:

    package com.swift; import java.util.Arrays; public class ArrayTest { public static void main(String[ ...

  6. JS window对象 返回浏览历史中的其他页面 go()方法,根据当前所处的页面,加载 history 列表中的某个具体的页面。 语法: window.history.go(number);

    返回浏览历史中的其他页面 go()方法,根据当前所处的页面,加载 history 列表中的某个具体的页面. 语法: window.history.go(number); 参数: 浏览器中,返回当前页面 ...

  7. stp域中两台switch互联接口出现两口均为root口 并且在现有stp区域中无法确定根桥设备位置;分析其原因并赋予解决办法

    stp域中两台switch互联接口出现两口均为root口 并且在现有stp区域中无法确定根桥设备位置:分析其原因并赋予解决办法 1.于上图描述了案例中当前组网环境的各交换机位置与stp状态情况  : ...

  8. SQL Server中的“最大并行度”的配置建议

    SQL Server中的最大并行度(max degree of parallelism)如何设置呢? 设置max degree of parallelism有什么好的建议和指导方针呢?在微软官方文档R ...

  9. 生产环境中使用Docker Swarm的一些建议

    译者按: 实践中会发现,生产环境中使用单个Docker节点是远远不够的,搭建Docker集群势在必行.然而,面对Kubernetes, Mesos以及Swarm等众多容器集群系统,我们该如何选择呢?它 ...

随机推荐

  1. 四、django rest_framework源码之频率控制剖析

    1 绪言 权限判定之后的下一个环节是访问频率控制,本篇我们分析访问频率控制部分源码. 2 源码分析 访问频率控制在dispatch方法中的initial方法调用check_throttles方法开始. ...

  2. Java还是程序员的金饭碗

    可能会存在一种更快,更简单的编程语言,但就目前来说,根据Stack Overflow的最新统计,“传统”的编程语言依然在赚着大把的钱.在2013年,招聘程序员时,搜索最多的技能关键字是Java,几乎有 ...

  3. jQuery 插件写法示例

    1.插件 taskStaticBar.js /**自定义任务进度条插件,用于发布任务单的显示进度 * 先初始化init() * 配置项type:1,任务发布方:type=2,生产方 * 然后传入sho ...

  4. 学会使用DNSPod,仅需三步

    学会使用DNSPod,仅需三步   第一步:在DNSPod添加记录 1.访问 https://www.dnspod.cn网站,在DNSPod官网首页的右上角,有[注册],如下图所示,点击[注册]按钮 ...

  5. 机器学习之路: python 决策树分类DecisionTreeClassifier 预测泰坦尼克号乘客是否幸存

    使用python3 学习了决策树分类器的api 涉及到 特征的提取,数据类型保留,分类类型抽取出来新的类型 需要网上下载数据集,我把他们下载到了本地, 可以到我的git下载代码和数据集: https: ...

  6. 80X86指令总结

    一.数据传送指令 指令名称 汇编语句格式 功能 影响标志位 传送move data mov opd, ops (ops) → opd:分为主存储器.通用寄存器.段寄存器,不可同时使用主存储器,类型要匹 ...

  7. 20162303石亚鑫 第十二周hash补充博客

    要求 利用除留余数法为下列关键字集合的存储设计hash函数,并画出分别用开放寻址法和拉链法解决冲突得到的空间存储状态(散列因子取0.75) 关键字集合:85,75,57,60,65,(你的8位学号相加 ...

  8. java的多线程之四(线程的操作)

    本文来自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17560467,转载请注明. 线程中断    线程中断涉及到三个方法,如下 ...

  9. UVALive 4423 String LD 暴力

    A - String LD Time Limit:3000MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Submit Stat ...

  10. 怎么样退出vi/vim编辑器

    怎么样退出vi/vim编辑器 先按   ESC   然后输入  w  q  :wq 就退出来了