使用go语言一个月的认识
lifc
2010-01-17
年底开始用go重写原来用d的一个小项目,经一个多月使用对go有了初步认识。贴一些go语言特性对比给想入门的朋友做参考,其中涉及自己常用的c++、erlang和d。因为对这些语言掌握程度都只能算入门水平,说错的地方还请大家指正。
+ 优势 - 不足 +/- 争议 + 属于编译型强类型语言,这点不必多说了 + 支持new+垃圾收集,不提供直接手动内存管理(但可以通过malloc外部库实现)。d支持手动管理(delete)及gc操作,可管理c分配内存区。erlang私有堆垃圾收集更高效。 + 内建轻量级进程(groutine)和消息传递(chan),可实现Actor模型。erlang直接支持,其他语言可以通过库实现,例如c++ CSP2以及d的Fiber和CSP +/- chan消息类型固定(除非使用empty interface传递任意类型),未提供erlang和scala类型的atom匹配,可通过switch模拟 +/- 不支持处理指定类型消息(erlang模式),必须按消息入队列顺序处理 + chan通道支持buffered/unbuffered工作模式,c++ CSP2同样支持 + groutine使用可自增小栈,大量groutine消耗内存更少。d的fiber以及c的ucontext模式同样使用固定栈,erlang表现更好 + 提供死锁检测,c++ CSP2支持DeadLock异常 + 通过defer语句实现资源释放,c++对象析构以及d的scope提供相似功能 + 不提供自动cast,类型转换需要显示操作:float(a_integer) + 支持闭包(closure),可以用来实现函数指针、匿名函数和函数嵌套。这年头似乎流行的语言多少都有支持::erlang提供fun这一基本类型,d语言有类似C#的delegate(d1.0没有保存上下文,2.0开始支持上下文完整闭包),至于c++各种技巧性实现就暂且不提了 + 动态类型,可以借助interface提取配合内建反射机制进行匹配,参见fmt库Printf实现 + 多返回值,d/c++甚至c都可以直接或仿真实现 + struct支持嵌入(数据继承)与匿名成员,调用指定receiver的函数时自动匹配(例如Mutex.Lock),但不支持c/c++的bit field,这一点和d相同 + 数组可动态增长,d语言内建支持,c++可以使用库实现 + 支持slice:提高数据引用效率,减少缓冲溢出风险,不足是不能作为l-value,与d的slice相比稍逊一筹 + 内建utf8编码string,构建后内容不可变,实现了基本字符串操作函数 + 支持包一级初始化,Plan 9的c编译器已经开始支持,d同样提供模块初始化功能 + 全部变量自动初始化成0,避免c/c++中出现的垃圾数据。d语言具有相同设计,想必主要是为了照顾gc,不然一大堆垃圾数据难以从中识别出有效指针 + 支持单元测试和文档生成:java、d、c++等语言同样内建或通过外部支持 + 语法改进(包括一些语法糖) 减少括号分号:for、if、switch等不需要括号,同时行尾不强制分号,分号仍可作为语句分隔用 for:替代while,不支持逗号语法,暂未发现do while替代方式。配合range提供foreach语法,可对数组、片断(slice)、map或其他实现interable接口的类型进行迭代操作,同时for ... range还可以循环接收chan消息管道数据直到管道关闭 if: if a = b; a {},和c语言中if (a = b, a)作用相同 break、continue:支持lable(d、java都有) switch:case语句支持逗号分割多个数值以及非常量表达式评估(gcc扩展支持范围匹配),当前case分支执行后自动break,使用fallthrough进入后续case select:同时等待多路事件源(chan通道) struct:分配、初始化语法灵活方便,取栈变量地址自动复制;支持struct嵌入(类似d等语言的mixin)以及匿名成员自动引用(记得Plan 9的c编译器好像也支持) var/=/:= 支持同时定义多个变量以及a,b = b,a交换语法,支持类型自动推测,和d/c++ 0x的auto效果相似 import:同时导入多个包,别名导入,名字空间访问方便 const:可以代替enum,使用iota根据开始定义的规则实现自增 goto:继续支持 ->:指针引用不用写->比c和c++方便些,这方便d早已统一 +/- 将赋值操作定义成语句而非c的表达式,j就是说b=(a=1)这种写法无效 +/- 支持指针但不支持指针运算(包括++/--),据说出于安全考虑 +/- 数值变量只支持a++而省略++a形式,且视为表达式(b=a++不再合法) +/- 独特的访问控制模型:首字母大写表示public属性,小写字母struct或成员只能在包内访问,这点有些fortran的感觉 +/- 将未使用引入(import)与变量视为编译错误,无用变量可以用类似erlang的下划线(_)乎略 +/- 内建map(同d语言),但不允许struct作为key,同样可以通过库实现 +/- 与众不同的对象实现方式:不直接提供类与继承,通过struct实现类。方法是定义包含receiver的func给struct绑定成员函数(须和struct放在相同包中),基本类型定义别名后也能绑定。 +/- 灵活但独特的interface,若struct实现了interface声明的方法则视为实现此interface。可以通过 InstanceInterface.(InterfaceType)动态提取接口,类似COM中IUnknown::QueryInterface;或用InstanceInterface.(Type)作unbox操作,将interface转换成Type类型 +/- 简约设计,没有范型、模板、函数和操作符重载、异常处理等,据文档说一些是出于设计考虑,另外部分功能酝酿中 +/- 未提供传统并发访问机制(例如barrier等),提供Mutex、Once、chan等同步手段,官方建议应通过消息而非共享内存进行共享。但消息传递同样涉及到数据驻留模型,go只将string设计成immutable,其它类型传递仍需要使用指针或创建拷贝,效率可能受到影响。erlang全部类型immutable(除ets),大块二进制数据自动通过引用计数在共享堆中共享,d并发模型设计则已经包含immutable/shared关键字(尚未完成) - 目前不支持Windows系统,mingw移植版本还只能编译简单程序 - select所等待的事件源必须是编译时预知的,不支持FD_SET的集合模式,且case处理优先级无法自定义(文档说明为随机,可能内建公平调度逻辑),这两点不如c++ CSP2灵活。 - 8g+8l/6g+6l: 目前生成代码效率稍低。已经列入语言发展计划,正在改进中。 全部import库静态链接至目标程序生成代码尺寸大 - 不支持asm嵌入,Plan 9的c编译器同样不支持。常见的c和d编译器都可以嵌入汇编,对于系统语言来说似乎有必要,不过Plan 9认为能够外联汇编目标文件已经足够了。 - 闭包是c模式的,不能隐含携带对象(struct)指针,使用略为不便 -- 可用库少,主要是缺少高级网络、gui、数据库访和媒体处理库。为解决goroutine调用外围代码产生的阻塞,包括libc在内的底层支持包全部需要重新实现(对内部轻量级调度进行封装),成为直接使用现有c/c++库的最大障碍 -- 使用ffi调用现有c代码不方便,需要编写接口go文件再通过cgo生成多个中间文件通过gcc编译链接产生最终动态库(使用$GOROOT/misc /cgo目录下Makefile模板),但目前ffi调用外部.so库路径固定指向$GOROOT/pkg/$GOARCH,难以脱离开发环境发布。 当前ffi方式只能单向调用c库(go需保持指针防止被垃圾收集,和JNI原理类似),c代码不允许直接回调go(可管道通讯模拟回调但使用不便)。这方面d语言稍具优势,d 2.0提供一定c++兼容性;erlang具备多种跨语言调用模型,R13 B03加入NIF使用更方便。 -- 不具备动态模块加载能力,暂时不适用于依赖动态插件技术的项目 -- 调度器尚不完善。为利用多核、多CPU并发计算能力需要手动设置环境变量GOMAXPROCS大于1,但会导致goroutine通过线程切换调度,ping-pong类测试效率急剧下降。此外进行1000万次token传递,1个goroutine耗时5秒,100个10秒,1000个goroutine传递就要20秒,说明除掉cache命中率下降因素外调度算法的效率也有优化余地(实现类Linux的O(1)调度),这方面和erlang差距还比较大(进程数量增加对效率影响较小)。 -- gccgo:基于gcc后端的另一个go编译器,编译不成功未实测,以下结论根据文档得出 代码效率更高(得益于gcc backend)但编译速度稍慢 可以直接和gcc代码链接 尚未实现垃圾收集,不能运行实际项目 goroutine通过pthread实现,调度开销大且栈空间占用多,无法大量使用 |
|
delphidoc
2010-01-18
兄弟真是高人
|
|
lifc
2010-01-19
刚开始学很多问题还搞不明白,写下来希望能够加深印象。
|
|
huylivecn
2013-04-21
厉害厉害,拜服
|