“十年磨一剑”,Cx经历十年的研究、沉淀、设计。。。
01.拥有ARX的强大功能,具备LISP的灵活、简便、动态等特性
02.非常方便与其他程序的沟通和调用DLL功能
03.支持反应器
04.高度动态
05.尽力静态
06.简洁的数据操作
07.支持DCL,MFC
08.支持ForEach,map
09.良好容错、除错能力
10.支持(自)覆盖/热替换
11.支持正则表达式
12.支持多线程编程、并行计算
(2):
13.实时可控的动态数据处理方式,高级指针、GC
14.内建大量数据类型及处理方法
15.支持高精度整数和高精度实数
16.类定义中的“简化型”多重继承、运算符重载、仿函数
17.模块化(#Package)编程
18.参考(Reference)/映射(Refect)型变量和数据
19.传址参数、署名参数(缺省参数)的函数定义
20.参数包(Valist)、闭包(Closure)、子函数
21.窗口函数的消息处理、事件反应、虚拟调用
22.方便编写键盘重设置(键盘宏)、成倍提高电脑操作速度
23.支持小型2D-CAM系统,方便编写CNC加工的应用程序
24.装载、命令行操作
25.其他说明
(3):(待续)
一种新的强大的脚本语言引擎,Cx语言,拥有C/C++语言的强大功能,又具备普通脚本语言的灵活、便利、简洁、动态、非编译等特性,已成功用在AutoCAD、ZWCAD和办公室软件WPS等,成为其强大、灵活的二次开发程序语言。本文介绍用在AutoCAD里的Cx。
01.拥有ARX的强大功能,具备LISP的灵活、简便、动态等特性
对于AutoCAD,其ObjectARX(以下简称ARX)的编译型C/C++程序,功能可以非常强大,但是,作为编译型的C/+C++程序语言,门槛高、编程繁琐、编译扰人、共享不易、版本敏感等,让不少人转到C#和JAVA,但是C/C++中有很多很值得人们珍惜和学习的东西!这些年来,AutoCAD每年升级一次,原有的ARX应用程序,就面临升级/版本兼容问题,这既会严重影响人们对CAD升级的欲望,也影响人们对ARX应用程序欲望。
Cx以ARX方式结合到AutoCAD中。Cx程序的书写格式、运作方式、数据结构等都非常类似C/C++程序,支持包括指针、回调函数、位域、MFC、Win32API等在内的C/c++大部分数据结构和功能,也可以进行内存直接处理的非常底层的操作。
与C/C++不同,Cx的内存处理基本上是自动的。与.NET的内存托管、JAVA的GC不同,Cx的内存处理是实时可控的。Cx里可以存在多个变量/结点同时拥有同一动态数据,却不会引发死循环而造成内存长期不能释放的问题。
文件句柄、资源句柄、CAD实体对象句柄等,如果Cx程序没有显示Close或释放,Cx会在拥有句柄的变量/结点的作用域失效时、或数值要被覆盖时,自动关闭或释放这些句柄。象句柄的打开与关闭、多线程中数据同步锁的取得与释放等需要成对的操作,Cx会严格控制,一旦程序异常中断,Cx会自动进行善后处理,没有及时关闭或释放的,会将其关闭或释放,这对于脚本语言来说非常重要。如果CAD实体没有及时close(),对CAD来说基本上是致命的崩溃、甚至烟消云散。
作为脚本语言,Cx同时又具备普通脚本语言的各种高级特性,与AutoLISP一样,很少受CAD平台的版本影响,编程、装载、调用、功能共享等,都非常方便。在功能、表达能力、运行速度等方面,CX显著超越AutoLISP。
在数据类型方面,Cx既支持C/C++方式的强制类型数据,也支持普通脚本语言的泛型数据。
先看看下面一个例子,这个例子取自网络牛人“雨中飞燕”提供的求1000阶乘算法的C代码,通过改写函数头,long前加一个点,就成为Cx语言程序了:
相信绝大多数的脚本语言无法书写表达如此精练的程序(不是可读性^_^)。这个例子足以显示Cx语言良好的表达能力、与C的亲近、相似程度。
Cx定义函数,以关键字def或function标识,函数名称可以用任何字符,当然包括使用中文,当使用"不规范字符"的名称时,用[],‘’,或""界定,上面定义的这个函数,用[]界定,名称是C:C00,如果函数定义用关键字public前置修饰,那么,定义的这个函数,会在CAD的命令队列中注册一个命令C00,这样,操作者在CAD命令行中输入C00时,就会启动相应的函数。这点,非常类似AutoLISP函数的定义。
ARX中的大部分类定义都可以映射到Cx中,在Cx中使用这些类,宛如在C/C++中使用,因此,熟悉C/C++,尤其熟悉ARX程序的人,对Cx程序应该不会陌生。
Cx操作CAD可以使用多种方式编程:ARX方式,ADS/LISP方式,ActiveX/VBA方式,C动态编译方式等,其中以ARX方式编程运行效率高,如果不是操作CAD,而是纯粹的数值计算,C的动态编译方式的运行效率高,它基本上等效C的静态编译程序。
下面以同时缩放5万个圆实体半径为测试实例,展示CX不同的编程方式。电脑配置:XP,CPU:Q6600@2.4GHZ(4核),RAM:2G。这也是本文所有测试使用的电脑配置。预先画好的5万个CIRCLE实体,启动相应命令时,选取该5万CIRCLE,再输入缩放值,比如2.0。运行结果如下表:
Cx(Arx普通方式)2971
Cx(Arx优化方式)2190.74
Cx(Ads方式)325010.94
Cx(ActiveX方式)8282.79
Cx(C的动态编译方式)27669.31
AutoLISP410913.84
ARX(C/C++编译型)940.32
ADS(C编译型)26879.05
无论是ADS的静态编译型的C程序,或动态编译型的C程序,还是AutoLISP程序,对CAD实体的操作,都是通过动态生成resbuf数据链间接操作,因此相当耗时,C程序的高效性在这里无法表现出来,如果只是数值运算,编译型程序会展现很好的运行效率,象上面The first程序C:C00的求1000阶乘中的循环部分,经过测试,得到如下结果:
Cx(普通方式)375
Cx(TrySpeed优化方式)172
Cx(动态编译方式)<10
C(静态编译)<10
可以看出,这里的动态编译的程序,其运行速度与传统静态编译型的C程序没有什么差别,显著超越非编译型的脚本语言。
ActiveX方式操作CAD,没有牵涉数据链,因此运行效率较高,但是ActiveX接口本身存在一定的效率损耗。
Cx程序展示出高度的灵活性和适应性,能够以各种方式混合编程,既可以根据实际运行效率的需要,也可以根据编程者自身的喜好、掌握的技能、水平状况编写相应的程序。无论ARX编程者,还是VBA编程者,或AutoLISP编程者,在Cx里都可以找到自己熟悉的编程方式。
优化后的Cx程序,其操作CAD的运行速度达到ARXC/C++编译型程序的一半,而其程序码的编写比C/C++程序要来得简洁。AutoLISP程序是所有程序中运行慢的。
各种程序码如下:
1)ARX方式,耗时297ms(毫秒)或219毫秒:
该程序处理5万个CIRCLE运行耗时297毫秒。
用Cx提供的AcDbObjectOpenArray类,把选择集中的所有实体一次性open(),再逐一操作实体,然后一次性close()所有实体。这样可以进一步提高Cx程序的运行效率,仅仅耗时219毫秒。
2)ADS方式,耗时3250ms(毫秒):
3)ActiveX方式,耗时828ms(毫秒):
4)C的动态编译方式,耗时2766ms(毫秒):
5)AutoLISP程序,耗时4109ms(毫秒):
6)C/C++(ARX)编译程序,耗时94ms(毫秒):
7)C(ADS)编译程序,耗时2687ms(毫秒):
该程序与“4)C的动态编译方式”{%%%%}中的程序完全一样。
再测试,以生成5万条直线为例子,得到的结果与上面的情况基本一样:
Cx(ARX方式)2971
AutoLISP13604.58
ARXC/C++编译型程序1090.37
ADS编译型程序10323.47
限于篇幅,这里仅仅给出Cx的ARX方式的程序:
02.非常方便与其他程序的沟通和调用DLL功能
Cx支持包括指针、回调函数在内的绝大部分C/C++数据类型,因此Cx调用普通DLL中的函数、数据非常简便,例如:
被LISP函数(vl_acad_defun)注册过的LISP函数,可以被Cx函数调用,比如有LISP函数:
这样就可以在Cx程序里直接调用(以操作符“!”标识调用的是LISP函数):
以public标识的Cx函数,会在LISP空间注册相应的函数,让LISP程序直接调用。因为Cx程序往往是定义在各自的命名空间,因此,LISP里注册Cx函数时,会在函数名前加空间名的前缀,比如:
Cx程序:
LISP程序:
但是,如果函数名是前缀C:,则不做追加前缀处理,而是与LISP语言一样,直接注册成CAD命令型函数。
Cx支持操作ActiveX接口,既可以通过该接口操作CAD,比如对上面对CIRCLE实体进行半径缩放的例子(C:04),也可以通过该接口,启动、连接诸如Excel.Application,或电脑系统中大量的存在的ActiveX构件,比如:
显然,通过ActiveX接口,让CAD与Excel等软件交换数据是很方便的。
下面给一个例子,展示如何用Cx的回调函数为参数调用Win32API的函数,具体是枚举AutoCAD里acadbtn.dll中所有图标BITMAP/ICON资源的名字,并且屏幕显示前十个名字。在声明Win32API函数时,回调函数的参数以void*类型声明。Cx支持类的成员函数构建回调函数,这样的回调函数类似C#中的delegate,能够调用对象里封装的数据成员。与C/C++不同,Cx里取得函数myFunc的地址是通过&.myFunc()格式。
输入命令C08启动上面定义的函数C:C08,得到如下输出结果:
BitmapNamesList:
ICON_16_2DOPTIM
ICON_16_3DCLIP
ICON_16_3DCLIPBK
ICON_16_3DCLIPFR
ICON_16_3DCORBIT
ICON_16_3DDISTANCE
ICON_16_3DFACE
ICON_16_3DMESH
ICON_16_3DORBIT
ICON_16_3DPAN
IconNamesList:
上面这段程序中有几个函数、方法定义中,对参数的类型做了强制限制,这是回调函数的需求。
03.支持反应器
下面展示的例子是建立AcEditorRector型反应器,当该反应器打开(ON)时,一旦完成OFFSET,COPY,MIRROR,ARRAY或–ARRAY命令后,该反应器会自动把刚刚生成的所有实体的层、颜色和线型的属性改成DWG当前值:
04.高度动态
Cx程序可以非常动态,象前面说的C程序的动态编译,后面将要说明的程序覆盖/“热替换”,都是很好用的动态特性。此外,函数名、变量名、类型名等标识符(名称)可以为任何字符组成,甚至可以计算。一般以前置符$作为特殊标识开始,比如:
在可以明确是标识符的地方,比如函数名定义处,前置符$可以省略。
Cx程序码可以动态构建,方式有多种:Lambda函数、普通函数体、字符串等,都可以动态插入运行。对于函数体、Lambda定义体的插入运行,与单独运行这些函数、Lambda定义体不同,比如定义体中的return语句,仅仅起到结束插入体程序运行的作用,不会对当前正在运行的函数产生影响。类似C语言中运行一个{。。。}局部结构的程序,不同的是Cx有带参数,这一点又象宏带入。一旦插入的程序段运行结束,期间产生的局部变量会马上释放。例如,有字符串:
可以动态执行:
等效执行字符串里表达的程序。只是这里的运行解释过程会比较消耗时间,如果对字符串里的表达式要求高效运行,可以先进行格式化处理:
再运行:
或
运行效率一般可以提高一个数量级。
Cx的函数定义,支持可变参数,比如,定义一个任意参数个数的加法函数:
其中内建函数ThisParamNo()取得当前函数myAdd的参数个数,内建函数ThisParam()则取得当前函数的参数地址/指针,这样,通过表达式: 变量B得到的值就是:10
05.尽力静态
Cx支持泛型变量,也支持强制类型声明的变量。明确变量类型,有利于Cx程序在运行过程静态处理,比如对高强度循环过程,通过语句:trySpeed{…}有可能显著提高程序运行速度:
上面定义两个函数,其中C:C10采用泛型方式,而C:C11采用强制类型声明,并且引入程序结构trySpeed{…},运行结果显示,C:C10耗时2234毫秒,C:C11耗时仅仅719毫秒。可以算出C:C11的运行速度是C:C10的3倍。
Cx语言会对一些语句进行静态优化,比如switch-case语句,因为进行静态优化处理过,其case匹配过程非常快速,即使存在数以千计的case语句,也可以在瞬间匹配到位。如果使用传统的if–elseif…语句方式,当case比较多时,会明显影响运行效率。
06.简洁的数据操作
Cx中的大部分数据结构,与C/C++中的数据结构是完全一致的,当与C/C++编译成型的Win32API的DLL模块进行沟通时,数据不需要进行任何转换,比如,Cx语言生成的字符串数组:
在内部数据结构分布上与C中的如下数组是完全一样的:
因此,当有C的库函数需要这种类型的参数时,可以把mArray直接赋值参数,Cx语言内部不需要做任何数据转换,如:
在Cx语言里对mArray数组元素的赋值,要比C里简洁、自在得多,不需要考虑内存泄漏问题,也不会发生内存泄漏,有关内存的释放,Cx语言会自动完成,程序员的编程仅仅是进行简单的赋值操作:
不要担心mArray[8]处原来是否存在字符串,如果存在,也用不着编程去释放这个字符串,Cx语言会自动、马上释放。
如果把某数组元素赋予变量v,实际上是让v指向该元素拥有的字符串:
当mArray[8]拥有的字符串更新时,原来的字符串自然失效,变量v的值会自动变为NULL。
调用Win32API函数时,往往会要求各种struct型参数,有相当部分在Cx内部有预定义,如果没有,可以用Cx程序直接书写,Cx支持位域、位对齐等,效果与C/C++中定义的完全相同。
有C/C++编程经验的人,应该可以从中看出Cx语言程序的简便方式对编程效率、品质的意义。
AutoLISP语言操作CAD,往往通过resbuf数据链,因为LISP语言的传值特性,对数据链的任何修改,都会引发重构整条数据链,造成明显性能损耗。Cx支持指针,可以修改resbuf数据链中任意数据结点而不必重构数据链,而且操作非常简单,比如,变量L1中有如下resbuf数据链:
(0123456)
如果要修改第三个数(从0位算起),比如乘8:
L1中的数据链变为:
(01224456)
也可以把第三个结点的数据直接赋其他类型数据的值:
L1中的数据链变为:
(012“Hello”456)
还可以方便操作resbuf数据结点亚结构中的数据,比如,变量L2中有如下resbuf数据链:
(012(11“X”“Y”)(22“U”“V”)3.14)
用如下表达式:
就可以把L2改成: (012(11“X”“Y-ABC”)(22“U”“V”)3.14)
Cx仅仅是通过数据指针修改相应亚结点的数据,其他结点的数据完全没有改变。操作很方便吧。
大部分脚本语言,都采用传统的所谓的垃圾回收机制调控资源,这个回收机制虽然已经改善很多,但是仍然存在一些问题,一个是垃圾回收过程的不可预测性;其次是编写程序过程需要很小心,否则可能会出现相互引用而造成内存永远无法释放;象数据链、数组之类的复合型数据,相关引用很多,会影响数据处理效率。Cx语言完全没有这些问题。
07.支持DCL,MFC
Cx除了支持AutoCAD提供的窗口控制语言DCL外,更大力度支持MicrosoftVisualC/C++的MFC编程。用MFC设计窗口,会比DCL强大、漂亮得多。由于Cx脚本特性,建立独立小单元的测试非常方便,学习、测试MFC特性是一件相当让人振奋的事。
Cx内部连接大量MFC类(class),可以直接使用这些类,无缝地继承这些类,虚拟函数、操作符重载、仿函数、枚举值,等等,都可以继承过来。对于窗口类,Cx处理消息要比C/C++语言简洁得多,不需要使用映射消息的数据结构和相应的宏。对于不同的窗口消息,Cx会自动调用相应名称的方法处理消息。Cx本身的类定义,支持“简化型”的多重继承,一个类里可以同时定义具有不同参数个数的构造函数,析构函数功能是健全的,在对象失效时,会及时启动。下面先给个简单的例子:
这里的操作,隐式调用了AcGeMatrix3d仿函数。
Cx中定义的类,如果继承自MFC的CObject之类的类,可以拥有MFC类CRuntimeClass,进行相当底层的操作,比如进行动态产生窗口,象下面的例子,将主窗口分割成3个子窗口,利用CRuntimeClass为每个子窗口动态产生Cview型窗口:
08.支持ForEach,map
作为脚本语言既酷、又富有效率的程序结构:ForEach和map,Cx大力度支持。对于ForEach结构,Cx的表达方式有:
1)for(xinexpress[,len]){…}
2)for(ptoexpress[,len]){…}
3)repeat(xinnum[,Zero[,Step]]){…}
其中express,可以是各种形式的数组结构、数据链、字符串、和CAD选择集,等等。这类表达式已经不断出现在上面例子程序中。它们不仅表达简洁,而且可以让Cx程序的运行更富有效率,For(xinexperss)中的x,不仅对应各个数据结点的值,很多时候是这些数据的参照(Reference),让这些结点数据直接进行存取,比如,有数组:
这里的$i为Cx的系统变量,自动记录了该for循环的序号。因此,现在A中各元素的值分别是:
0,1,2,3,4,…,998,999。
for(ptoexpress){…}结构,用于操作复杂的数据结点,比如ADS的resbuf数据链,p是指向各个数据结点的高级指针。通过该指针,可以方便操作所指数据结点的各种值。比如,建立如下resbuf的LIST数据链:
得到LIST数据链:
((0“Sa”)(1“Sb”)(2“Sc”))
进行to的forEach操作:
LIST数据链L的内容变为: ((0"Sa-x0")(1"Sb-x1")(2"Sc-x2"))
对于Map的结构,Cx的表达方式有:
1)map_in(L1,L2,…){
functionPtr;
}
2)map_to(L1,L2,…){
functionPtr;
}
其中L1,L2,…可以为各种形式的数组结构、数据链、字符串、和CAD选择集等,FunctionPtr为Lambda、函数指针、或闭包(下篇博文有专门论述)。比如有3个数组:
。。。初始化A和B各元素的值后,
完全等效C/C++中的表达式:
对于map_to,与for(pto…)的情况一样,用于操作复杂的数据结点。
09.良好容错、除错能力
作为脚本语言,良好的容错性是很重要的,不像编译型程序语言的程序错误可以在编译过程发现和更正,脚本语言的很多错误只能在运行过程发现。因此,脚本语言应该有一定的纠错、“容错”能力。比如在前面讲到的程序在异常情况下中断,需要能够自动对要求成对操作进行善后处理。
Cx中的对象变量或对象结点,一般都具有双重性:指针和参照,因此,Cx对方法的调用,一般即可以用点操作符“.”,也可以用指针操作“->”。
Cx的标识符不区分大小写。函数名称、变量名称、类型名称等可以同名,因此不用担心变量名称的随意使用而可能导致同名函数、类型的失效。
采用下面程序结构,可以处理程序中可能的出错:
1)try{};
{}中出错时,跳出{},继续执行下去。
2)try{1}except{2};
{1}中出错时,跳到{2}中运行,如果没有出错,则跳过{2}。无论如何,程序都会在{2}后继续运行下去。
3)try{1}finally{2};
无论{1}中是否出错,都会进入{2}中运行,然后程序都会在{2}后继续运行下去。
010.支持(自)覆盖/热替换
Cx的函数定义,类定义,结构定义等,都可以进行(自)覆盖/热替换,程序运行过程可以随时随地装载新的程序,无论新的程序中的各种定义是否与旧的定义重叠,都没有关系,新、旧定义可以并存,旧的定义体仍然可以正常运作下去,新的定义也可以开始新的操作。即使在多线程复杂情况下也可以进行正常装载!
AutoLISP支持函数自覆盖特性,这是非常有用的特性,利用该特性,数以千计的功能、函数,可以在真正需要的时候才自动、随时随地装载,而不是在宿主程序启动时一次性装载而严重影响启动时间、也造成内存浪费。
用脚本语言进行大型软件的二次开发,说简单很简单,可能仅仅一行代码就可以完成一项任务,它的简洁、它的灵活性,是编译型语言不能匹敌的!但是二次开发、尤其脚本语言进行二次开发,说困难也困难,因为运行环境很恶劣,很难预料、不好控制,操作者的操作可能很随机,甚至是恶意的,如何去适应?二次程序工程师可能不止一个,素不相识的工程师的程序之间如何协调、共处而不冲突?二次开发的程序可能非常多,而可能调用的功能很随机,在不能把所有功能程序装载、甚至绝大部分功能程序都没有装载的情况下,操作者如何随心所欲、让宿主平台自动、简易调用需要的功能?程序重载、覆盖,是否会影响正在运行的程序?等等,所有这些,让人眼花缭乱。
Cx语言的产生,当初的宗旨就是为了解决上面这些问题。很庆幸、也很意外的是,上面的这些问题,现在在Cx语言里已经不成问题了。
011.支持正则表达式
例如下面是一个定义有效日期格式的正则表达式(考虑了润年影响,摘自网络文章):
正则表达式是一种很特殊的语言,看起来怪异,不容易书写和上手,但是很有用,可以在网上找到不少实例。
012.支持多线程编程、并行计算
作为脚本语言,Cx没有象Python那样的“全局锁”(GIL),所有运行于各线程的Cx程序,都是真正的系统级线程程序,程序语句独立、安全。一般脚本语言的内部实现,需要牵涉太多公共数据读和写,在多线程状态下要安全实现数据共享,往往需要付出太多性能代价,Cx非常成功解决了这个问题,代价很小,很安全,甚至在其他情况下,比如10个线程同时在同步锁协调下为某个整数增值操作,同步协调造成的运行速度损耗不会超过3%!如果采用传统同步锁方法进行同步操作,运行速度损耗可能高达85%!
下面给个比较测试的例子。Cx提供一种同步锁:单写多读同步锁wrLock,允许同时很多读操作,而写操作具有排他性,一次只允许一个写操作。比较用的同步锁程序来自JeffreyRichter编写的类CSWMEG,也是单写、多读同步锁。为了便于比较,该类已经被Cx关联,可以直接在Cx里调用。这里先给出结果,再列出相应的程序。程序的结构以Cx支持的并行计算方式书写,对数据成员m_res共做1千万次增1操作。其中类CmyPa_1采用wrLock同步锁,而类CmyPa_2采用CSWMRG同步锁。函数C:13和C:14都调用类CmyPa_1,分别使用单线程和10个多线程操作,函数C:15和C:16都调用类CmyPa_2,方别使用单线程和10个多线程操作,结果如下(消耗时间:ms毫秒):
同步锁单线程多线程比较(单线程/多线程)
wrLock(Cx)5,3445,48497.45%
CSWMRG(ByJeffreyRichter)5,75039,01514.74%
从中可以看出很大的差异!显示Cx语言提供的同步锁优越的性能!需要指出的是,两种同步锁下的程序运行过程,CPU占用率都不太高,在20~30%之间徘徊。
多核PC已经普及几年了,能够利用多核优势进行编程的程序语言不多,脚本语言就更少了。为了有效使用多核,要求程序能够支持真正的多线程(Multihread)!
有一些脚本语言声称支持多线程编程,但是它们即使能够运行于多核,却达不到提高程序运算效率的目的,为什么?象现在很流行的脚本程序语言Python和Ruby,其多线程的运作是通过全局锁(GIL)调控,在GIL控制下,多线程的运行是畸形的,在同个时间段,只允许一个线程取得GIL,处于运行状态,其他线程是处于休眠状态!可想而知,在GIL控制下,这些多线程合计的运算效率,与一个普通单线程情况下的运算效率不会有多大差别!因此,人们一直期望能够移除这个GIL。Python到版本3.1、Ruby到版本1.9都没有能够移除掉GIL,倒是基于微软。NET平台的IronPython和IronRuby没有采用GIL,这是一大进步。
脚本语言的实现,一般内部存在大量需要共享的数据,虽然在多线程环境里数据共享方法很早就有,也健全,但是就是存在明显的效率问题,线程一多,问题更大,可能会象死机一样,这是个令人非常头痛的问题。有人抱怨CPU提供的“同步”机制太弱,Intel等硬件厂商把无法在硬件上再提升速度的问题,转嫁给软件业。不过,这也反映出,软件的发展跟不上硬件的发展。
Cx程序语言的成功,在一定程度上是受益于多线程同步技术的探索取得突破性进展!为了适应多线程需要,Cx语言的架构就是基于多线程。它支持系统级多线程,能同时运行于CPU多核,不仅支持MFC的工作者(Worker)线程,而且支持用户界面(UI)线程。
上面的例子是围绕同步锁激烈竞争情况,下面给个没有同步竞争的多线程平行计算性能比较测试结果,与Python、IronPython对比,测试过程是求从1到5千万的和。分别测试在单线程和有10个线程并行计算的结果如下(消耗时间单位:ms毫秒):
单线程10个线程(单线程/10线程)的比值
Cx18755473.43
Pythonv3.1474744701.06
IronPythonv2.0673540301.67
限于篇幅,省略源程序。可以看出,Python在多线程下并行计算得到的效率提升很少,几乎可以忽略不计。在多线程下IronPython的并行运算效率有些提升,而效率提升很多的是Cx语言!Cx语言的运算效率与核数量几乎成线性关系,核利用率高达85.75%(3.43/4)。从IronPython的结果看,感觉其内部运行机制中还是存在类似GIL的东西,只是效果比真正存在GIL时改进一些。
AutoCAD的操作不支持多线程,但是在AutoCAD里仍然可以正常运行多线程,只要这些多线程不牵涉操作CAD。