我们知道 D 语言能够直接调用 C 函数,看起来好像 D 语言直接构建在 C 语言内存模型上似的。如果我们毫不在乎多核——大规模并行架构所带来的处理能力,或许这还是个好消息,就这样用好了。但如今多核时代已经来临,而 C 语言的那套处理方式则已极端过时和漏洞百出。其他的过程式和面向对象语言仅仅为此做了点点的改善。再就是函数式编程语言的情况,它依赖于不变性优雅的回避了很多并行相关的问题。
作为一个相对新生的语言,D 语言处在一个相当有利的形势。当它进入多线程世界时,可以综合考量、取舍很多东东,而且 D 语言的内存模型在某一方面和很多其他语言完全不同。你可以思考一下,经典的线程是不是如下这样工作:你调用了一个原语来启动一个新线程,然后新线程能够立即看到和访问程序中的任何数据。作为一个可选项,且按照依赖操作系统的晦暗模糊来说,线程也可以获得所谓的线程私有数据来为己所用。总之呢,内存默认是被所有线程所共享的。这种方式昔日已经引起人间腥风血雨无数,直到今天仍然在继续造就新的人间地狱。当初之所以留下这么多人间恨事,主要是因为并发更新的笨拙本质:它很难被正确追踪和同步,以至于不能自始至终的良好保持数据。但是人们仍然趋之若鹜,因为共享内存的观念非常接近于底层硬件的真实模型,而且使用得当的话,效率也非常高。现在是脱离这人间地狱的时候了。今天借助于内存容量的快速增长,需要共享的机会有所减少。关于现代硬件的事实是,处理器之间通讯通过深层分级存储器制度。而这其中每个核心很大一部分保持私有。因此共享内存方式也很难再发挥作用,它快速成为程序变慢的方式之一。因为存储器物理移动的次数和距离也快速增加了。
当传统命令式语言和这些问题缠斗的时候,函数式语言从数学中汲取智慧,另辟其径:命令式傻小子们,我们根本不关心硬件模型,而只关心纯粹的数学模型。因为数学大部分不会变化,而且时间恒定,因此成为并行计算的理想候选人。(想象一下,那些第一批从数学家转程序员的家伙听到并行计算的时候,肯定会直拍自己的前额:“等一下,等一下...”译注:我对 Andrei 老大讲笑话的水平真是不敢恭维)在函数式编程圈子里都知道这样的一个计算模型天生喜爱无序、并行执行,但是它的潜能直到近来才被挖掘出来。今天,越来越清楚地显示函数式的,mutation-free的编程方式至少会成为并行程序设计的一部分。
那么 D 的定位呢。D 对于并行有个基本的必要概念:
内存默认线程私有,按需共享。
在 D 中,所有的内存都是默认线程私有。即使是那些丑陋的全局变量也会被分配给每个线程。如果你想要共享,就需要在对象前面加上 shared 修饰符,这也就是说,该对象可以立即被其他线程可见。更为重要的是,D 语言类型系统了解共享数据,且限制它能够做什么以便确保同步机制能自始至终地被正确使用。这个模型可以很好的避免那些默认线程共享编程语言所带来的诸多同步棘手问题。在那些语言中,类型系统根本无法知道哪一个数据需要共享,哪一个不需要。所以它只能信任程序员,让他们来适当的标明共享数据。但是这样,就会产生很多麻烦,各种各样的场景都需要特别规则说明,比如非共享数据,已标明的共享数据,未标明但实际是共享数据,还有混合情况。Oh,yeah...
多核支持目前是非常活跃的一个研发领域,真正实际的良好模型还没有发现。D 语言把线程私有内存模型作为基础设施,只是为以下这些基础设施做些方便之处:纯函数、lock-free 原语,旧有的基于锁机制的良好编程机制、消息队列(计划中)等等。更高级的特性,比如所有权类型也在讨论中。
译注:基本上第3部分除了“内存默认线程私有,按需共享”这句之外,没多少东西,呵呵。依据业界多年的经验来看,并行这块领域根本没有银弹可言。