`
hqs7636
  • 浏览: 215670 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

D语言并发编程特性前瞻

阅读更多
http://wangyuanzju.blog.163.com/blog/static/13029200871734854243/


06年以来我就一直关注D语言的发展,D语言立志成为C/C++之后的主流系统编程语言,拥有垃圾收集等很多很强大的特性。D语言在07年初发布了1.0版,现在正在设计其2.0版。虽然有了所谓的1.0版,目前D语言还是一门非常小众的语言,正处于快速改进演化之中,因此已经吸引了Andrei Alexandrescu等众多C++牛人的目光,这些人正将D语言作为一个实验场,将很多由于C++的历史包袱而无法在C++中实现的特性和功能加入到D语言中。

今年以来,D语言之父Walter Bright其Andrei Alexandrescu等D语言的设计者们的大部分精力都在研究怎么在D语言中更好的支持并发编程这一非常具有挑战性的课题。选择这一方向的原因是非常明显的,免费的午餐已经结束,世界已经进入多核时代,在可见的未来CPU的核数只会有增无减,并发编程将越来越普遍。所有的编程语言都想在这一场并发编程大战中拔得头筹,Java在5.0中引入了concurrent包,并千方百计的优化锁定和并发垃圾收集的性能;C++0x定义了内存模型并将加入线程和同步机制的内建支持;以前很少有人关注的Erlang等函数式编程语言由于容易实现并发来了个咸鱼翻身;还有一堆脑子少根筋的人在那里琢磨几乎很难写正确的无锁式编程;另有一堆胆大包天的人研究什么Software Transactional Memory。好一场并发编程大战正进行的如火如荼,鹿死谁手还未可知。

据我有限的了解,D语言面向并发编程的设计主要分为以下两个方面:
1、invariant数据结构和pure函数
2、通过shared关键字明确标识可能被多线程并发操作的共享数据结构和只可能被单线程操作的私有数据结构;

invariant数据结构指的是一但初始化,其值在程序运行期间即不再会被修改的数据结构。D 中的invariant数据结构具有传递性,即invariant对象指向的对象也是invariant的。这与C++中的const和Java中的 final有本质的区别。C++中的const只是数据结构的一个只读视图,你不能通过一个const指针去修改对象的状态,但其它线程却可能通过非 const指针来修改状态。Java中的final只是一个不可变的引用,不保证其引用的对象的内部状态不发生变化。pure函数是指其输出只依赖于输入的函数,即无副作用的函数。借助于invariant数据结构,D语言可以对函数是否为pure进行静态检查,只有那些所有参数都是 invariant对象或int等基本函数,并且函数中没有访问任何可变全局变量和调用非pure函数的函数才是pure函数。invariant数据结构和pure函数的主要作用是实现无副作用的函数,从而像函数式编程一样,编译优化时自动生成并发控制的代码。函数式编程语言中,所有的数据结构都是不可变的(所有修改都是创建一个新的复本,不会修改原对象),并且函数的输出只依赖于输入,这使得(理论上)编译器可以自动生成很多并发代码而保证程序的正确性。D语言不是函数式编程语言,但通过invariant数据结构和pure函数,也能得到函数式编程语言易于并发的良好收益(当然从语法形式上与函数式编程语言还是大不相同的,而且我估计用起来不会有函数式编程语言那么方便)。

invariant与pure的引入曾在D语言社区引起广泛的争议,但shared关键字则更加大胆,这一解决方案需要开发人员精确的标识一个数据结构是否可被多线程访问,从而大大的提高并发代码的安全性,但很可能会导致很多原来的代码不能执行,也会给程序员带来一定的负担,因此引起了更大的争议是不足为奇的。

上述解决方案似乎是由C++大神Andrei Alexandrescu提出的,根据这一方案,如果程序中的某些对象会被多线程并发访问,那么在创建时必须使用shared关键字指定这一对象为可共享的。与invariant相同,shared属性也具有传递性,即shared对象引用的任何对象也是shared的,因此程序从一个shared对象出发,将不可能访问到非shared的对象。

shared关键字的一大好处是可以防止不经意的共享访问,提高并发程序正确性。D语言的设计者们认为,那些面向并发访问的数据结构所有访问入口都会用synchronized关键字同步,使用时通常不会出错,通常导致程序出错的是那些本来没想到会被多线程并发访问的对象实际上被并发访问了。通过强制使用shared关键字,这类情况将不允许出现。

使用shared标识的另一大优点是可以进行更高效的垃圾收集。根据Java的应用经验,垃圾收集确实能非常大的提高应用开发(尤其是公用程序库的开发)效率,但垃圾收集的性能却一直是个头疼的问题。大型内存堆的垃圾收集时非常耗时,且或多或少都要停止所有线程(所谓stop the world),在内存越来越大,CPU核数越来越多的环境中,这一问题只会越来越严重。其实,即使是在高度并发的应用程序中,真正需要被多线程并发访问的数据通常是很少的,大部分数据仍然只会单线程访问,通过标识shared和非shared数据,系统将可以采用更好的内存分配策略和垃圾收集机制,非 shared数据在线程私有的堆中分配,垃圾收集时不需要停止其它线程,只有少量的shared数据才需要在共享堆中分配。

使用shared标识的第三大优点是可以有助于实现高效的对象锁定机制。 D语言与Java类似,都使用所谓的object monitor机制,大致说起来就是所有的对象都可以被锁定,对象锁信息存放在对象内存头中。在Java中,由于系统不清楚一个对象是否真的会被多线程并发访问,很多时间会付出不必要的加锁/解锁开销。比如Java的集合框架中的很多类都设计为可被多线程并发操作,这些类的函数都会加上 synchronized关键字,即函数执行前会加锁,返回时解锁。应用在使用这些类时,即使对象只会在一个线程内部用用,加锁/解锁的开销也不可避免。而在D中,使用shared标识后,对象是否以shared方式创建的信息会记录中对象内存头中,这样在对象加锁时,用一个非常简单的判断就可以发现非shared对象,从而跳过加/解锁操作(锁定非shared对象是无意义的)。由于对象是否以shared方式创建信息在对象创建后不会发生改变,这一判断都不需要用于CAS操作,非常快。这样,公用库的设计都将不再需要因为性能的原因为同一个数据结构实现同步和非同步两个版本(如Java中的Vector和ArrayList,StringBuffer和StringBuilder)!

invariant和pure特性已经加入到D语言2.0分支中,已经基本稳定。shared关键字的问题已经经过了长时间的讨论,目前已经决定这一特性将会被接纳到D 2.0中,但目前还没有实施,据Walter Bright估计,这一功能将在接下来的3个月内逐步实现。由于这一功能还没有实现,很多细节目前还不清楚。
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics