Delphi中的延时

从其它平台迁移而来 开发过程中经常会需要使用到延时功能,Delphi中有不少实现延时的方法,网上已有不少文章做过说明和分析,但本着实践出真知的态度,还是亲自动手研究一番心里比较踏实。 常用的延时方法 Sleep Sleep(n),延时n毫秒,延时过程中程序不响应,一般延时较小时使用。 在主线程中使用,延时较大(100+)的话会起程序假死,一般在子线程中使用较多。 无论在主线程还是子线程中,延时较长的话(如 2000 ms),一般不一次性Sleep(2000),而是分多次循环Sleep。有时为了能在延时过程中响应外部消息,还会加上Application.ProcessMessages;,如: 1 2 3 4 5 6 //延时 2000 ms for i := 0 to 19 do begin Sleep(100); Application.ProcessMessages; end; Timer Timer为定时器,用于周期性地执行某个处理。也可用来实现延时,延时过程中不会引起程序假死, GetTickCount GetTickCount返回从操作系统启动到当前所经过的毫秒数,一般用于计算代码段的用时。配合循环使用也可达到延时的功能。 1 2 3 4 n := GetTickCount; repeat Application.ProcessMessages; //若延时过程中需要响应消息可加上此句 until GetTickCount >= n + ms; //ms为延时的毫秒数 注意:使用以上代码进行延时的过程中,CPU使用率会异常地高(事实上,不加限制一直跑的循环都会导致CPU使用率过高)。 小结 以上是对Sleep、Timer和GetTickCount用于延时的简单说明,个人经验:通常较小延时的场景用Sleep,较大延时的场景用Timer,评估代码段耗时的场景用GetTickCount。至于为什么这样用,以前是不清楚的,但通过对三者的精度分析,目前已知晓来龙去脉。 延时精度分析 上文已经提到,一般用GetTickCount来分析代码段耗时,但由于本次GetTickCount在被测行列,故另寻他法。 本次测试假定系统时间是足够精确的,因此使用Now分别在延时前后获取系统当前时间来进行耗时评估。 为使测试更具代表性,每个测试点测试100次,取算术平均值。 在1ms~100ms内,测试点步长为1ms,在100ms~1000ms内,测试点步长为10ms。 为尽可能减小干扰,测试过程中未使用Application.ProcessMessages;,也未使用并行。 测试结果 XE10编译,Win10下运行,经过近5个小时的测试,结果终于出炉了。 延时(ms) Sleep GetTickCount Timer 1 1.70 15.58 15.64 2 2....

2019-12-13 23:40:41 · 8 分钟 · 慢步道人

从Delphi到Go——方法

从其它平台迁移而来 结构体的方法 Delphi Delphi结构体的方法与类的方法几乎是一致的,主要区别是内存的管理方式和可见性不同。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //定义 type TMyStruct = record No: Integer; Name: string; function ToString: string; end; //实现 function TMyStruct.ToString: string; begin Result := Format('No:%d, Name:%s', [Self.No, Self.Name]); end; //调用 var ms: TMyStruct; s: string; begin s := ms.ToString; end; Go 方法其实就是加了接收器的函数,语法如下: 1 2 3 func (接收器变量 接收器类型) 方法名(参数列表) (返回参数) { 函数体 } Go结构体的方法无需声明,直接实现即可。...

2019-12-02 22:56:02 · 2 分钟 · 慢步道人

从Delphi到Go——异常处理

从其它平台迁移而来 Delphi try…finally…end 1 2 3 4 5 6 //创建、打开、加锁等 try //具体处理 finally //释放、关闭、解锁等 end; raise 1 raise Exception.Create('异常信息'); //手动抛出异常 try…except…end 1 2 3 4 5 6 7 8 9 try //可能产生异常的语句块 except //异常的相关处理 on E: Exception do begin //对应类型的异常的处理 end; end; Go defer 加defer的语句会延迟到函数调用结束返回时才执行,相当于finally...end区。存在多个defer语句时,最先出现的总是最后才执行。 1 2 3 4 5 func F(){ //打开、加锁等 defer //关闭、解锁等 //具体处理 } panic 1 panic(异常信息) recover 1 2 3 4 5 6 7 func FF(){ defer func(){ e := recover() //异常处理 }() //可能产生异常的语句块,或调用 panic() 抛出异常 } 虽然panic/recover组合可以模拟try....

2019-12-01 22:35:02 · 1 分钟 · 慢步道人

从Delphi到Go——函数的可变参数

从其它平台迁移而来 Delphi 事实上,Delphi并没有什么可以直接为函数传递可变参数(数量可变、类型可变)的语法,但是并不是说不可能实现,最常用的Format()函数就是最好的例子。 虽然不能直接传递可变参数,但是通过一种叫做可变类型的开放数组即可实现为函数传递数量不定、类型不一的可变参数。 可变类型 可变类型不是变体类型,而是一个记录类型TVarRec,在System单元中的定义如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 TVarRec = record { do not pack this record; it is compiler-generated } case Integer of 0: (case Byte of vtInteger: (VInteger: Integer); vtBoolean: (VBoolean: Boolean); vtChar: (VChar: _AnsiChr); vtExtended: (VExtended: PExtended); {$IFNDEF NEXTGEN} vtString: (VString: _PShortStr); {$ENDIF !...

2019-11-27 22:24:05 · 1 分钟 · 慢步道人

从Delphi到Go——匿名函数

从其它平台迁移而来 早期的Delphi版本是没有匿名函数的,不过可以定义一个函数类型来实现类似的功能;后期的版本已经支持匿名函数,随用随写。Go天生就支持匿名函数。 Delphi 函数类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 //声明函数类型 type TMyProc = procedure(A: Integer); //过程 TMyFunc = function(x: Integer): Integer; //函数 //定义符合函数类型的函数 procedure MyProc(A: Integer); begin ShowMessage(IntToHex(A)); end; function MyFunc1(x: Integer): Integer; begin Result := x + x; end; function MyFunc2(x: Integer): Integer; begin Result := x * x; end; //使用 var mp: TMyProc; mf: TMyFunc; begin mp := MyProc; mf := MyFunc1; mp(mf(99)); end; //作为参数进行传递,这才是函数类型最主要的使用方法 procedure Test(x: Integer; Func: TMyFunc); begin ShowMessage(Func(x)....

2019-11-03 12:14:24 · 1 分钟 · 慢步道人

从Delphi到Go——列表

从其它平台迁移而来 Delphi中最基本的列表是TList类和TList<T>泛型类,还有线程安全的TThreadList类和TThreadList<T>泛型类,底层实现是数组。Go用的是container/list包,内部实现是双向链表。 Delphi TList TList里存的是指针,使用时注意处理好指针即可。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 //声明 var l: TList; //构造 l := TList.Create; //添加 l.Add(p); //元素个数 n := l.Count; //列表容量 cap := l.Capacity; //取值 p1 := l.Items[0]; p2 := l.Extract(p1); //找到指针p1并从列表中取出,列表中将不再有p1,若其后还有元素,则前移填充空缺。 p := l.First; //取第一个元素 p := l.Last; //取最后一个元素 //查找元素的索引 i := l.IndexOf(p); //修改 l....

2019-10-30 21:58:48 · 2 分钟 · 慢步道人

从Delphi到Go——字典

从其它平台迁移而来 字典,又称为哈希表,是一种能够快速寻找值的理想结构。Go语言中对应的数据类型是map,Delphi中是TDictionary泛型类。 声明 Delphi 1 2 uses System.Generics.Collections; var 字典名: TDictionary<键类型, 值类型>; Go 1 var 字典名 map[键类型]值类型 初始化 Delphi 1 字典名 := TDictionary<键类型, 值类型>.Create(初始容量); Go 1 2 3 4 //使用make构造 字典名 = make(map[键类型]值类型, 初始容量) //直接赋初值 字典名 = map[键类型]值类型{键1: 值1, 键2: 值2} 元素操作 Delphi 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 var m: TDictionary<Integer, string>; //构造 m := TDictionary<Integer, string>....

2019-10-28 22:09:02 · 2 分钟 · 慢步道人

从Delphi到Go——数组

从其它平台迁移而来 静态数组 一维数组 声明 Delphi 1 var 数组名 : array[索引范围] of 元素类型; //索引范围是子界类型,格式为:下限..上限 Go 1 var 数组名 [数组长度]元素类型 Delphi的索引范围可以是任意的子界类型,而且是包含上下限的闭区间。子界可以是任意的序数类型(整型、字符型、枚举元素等),例如:0..8、5..11、'a'..'z'等。子界元素就是数组元素的下标。 Go的数组长度只能是整型,下标为0~数组长度-1。 初始化 Delphi 1 2 var 数组名 : array[1..N] of 元素类型 = (元素1, 元素2, ……, 元素N); //如果先声明后赋值的话,赋值时就需要遍历数组对每个元素分别赋值 Go 1 2 3 4 5 6 7 8 9 10 11 var 数组名 [N]元素类型 = [N]元素类型{元素0, 元素1, ……, 元素N-1} //由于初始化时元素个数已知,以上代码也可写为: var 数组名 [N]元素类型 = [...]元素类型{元素0, 元素1, ……, 元素N-1} //如果先声明后赋值的话,写法如下: var 数组名 [N]元素类型 数组名 = [....

2019-10-08 22:03:53 · 3 分钟 · 慢步道人

再探Delphi字符串

从其它平台迁移而来 闲来无事,又开始扒拉起Delphi的源码,这次发现一个比较有意思的函数StringCodePage,作用是返回传入字符串的CodePage。至于什么是CodePage,暂且认为是字符编码吧。 先测试一把: 1 2 3 4 5 6 7 8 9 10 11 12 13 var s1: AnsiString; s2: WideString; s3: UTF8String; cp1, cp2, cp3: Word; begin s1 := '123abc中国'; s2 := '123abc中国'; s3 := '123abc中国'; cp1 := StringCodePage(s1); //936 - GBK(简体中文) cp2 := StringCodePage(s2); //1200 - UCS-2LE Unicode 小端序 cp3 := StringCodePage(s3); //65001 - UTF-8 Unicode end; 来看下是怎么实现的: 1 2 3 4 5 6 7 function StringCodePage(const S: UnicodeString): Word; overload; begin if S <> '' then Result := PWord(PByte(S) - 12)^ // StrRec....

2019-10-06 04:43:45 · 4 分钟 · 慢步道人

从Delphi到Go——基础

从其它平台迁移而来 废话 长期从事Delphi开发,虽不敢说精通,但说很熟悉还是相当有自信的。不过,只会一门语言,而且还是这么老的语言,更是在大天朝很小众的语言,总感觉自己离饿死街头没多远了,所以趁着还没老再学个潮点的吧。 先前考虑过Python,初步了解后觉得不太适合自己: 解释型语言:部署时得先搞个运行环境,发布的程序就是源码本身,再加上这个执行效率,怎么想都还是编译型语言更合适。 动态语言:无需声明,拿来就用,这已经很不合习惯了。想想一个变量,前一秒还是浮点数,下一秒就成字符串了,再一眨眼又成某个对象了……虽然一般不会有人这么写,但是挡不住手误啊,还是把这种小细节交给编译器更让人放心。 所以,对于有点强迫症和洁癖的自己,最后还是选了Go,比较符合已有的编程习惯,学习成本应该相对会低些吧。 至于Go嘛,想学是已经很久了,但由于种种原因却迟迟未开启,不过终究还是要迈出这一步的,所以就搞这么个系列来记录吧,一方面算是自我督促,另一方面也算是一种交流吧,当然,若一不留神帮上了谁,那自是开心极了。 言归正传 已经初步了解过了Go,说来和Delphi还是有不少相似之处呢,从Delphi转向Go应该会比较轻松吧。 工程结构 Delphi的工程算是比较自由的,源码的话,只要把单元路径引了或是直接包含进工程单元里就可以了,编译出的dcu和最终的exe指定下路径也就没问题了,通常我都使用下面这种结构: 1 2 3 4 5 6 7 8 9 10 11 Project/ bin/ src/ dcu/ mod1/ *.dfm *.pas mod2/ *.dfm *.pas *.dpr 不过,每一个工程都要设置,而且我习惯将Debug和Release设置完全一样,也还真是够烦的。 Go就没得选了,只有一种结构: 1 2 3 4 5 6 7 8 9 10 11 Project/ bin/ pkg/ src/ *.go mod1/ *.go *_test.go mod2/ *.go *_test.go 整体和我原有的习惯差不多,还是蛮容易接受的,不过倒是要把这Project的路径加入到GOPATH系统变量里让人有一点小不爽。但是Go可以直接把测试都写了,这点还是蛮让我惊喜的,毕竟用了这么多年Delphi也没写过一行像样的测试。 源码结构 Delphi典型的源码结构是这样: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 unit Unit1; interface uses ....

2019-09-09 23:30:04 · 7 分钟 · 慢步道人