设为首页 加入收藏

TOP

1.2.3 可扩展性数据共享
2013-10-07 15:21:02 来源: 作者: 【 】 浏览:66
Tags:1.2.3 可扩展性 数据 共享

1.2.3   可扩展性数据共享

通常来说,任务之间总免不了要共享数据。但问题是,这些任务在并行化运行的过程中很有可能会对同一块内存区域执行竞争式访问和更新,使得相关的数据变得非常不可靠。这种不在我们计划内的数据竞争所带来的后果往往是灾难性的。要想避免这个问题,解决方案之一就是使用线程同步技术。

也许,你对那些通过阻塞线程来实现的同步并发线程技术并不陌生,比如锁、原子性对比交换操作、信号量等都属于这一类技术。这些技术也确实都能有效地实现访问共享资源。事实上,我们对于数据共享的第一反应似乎也依然是加锁这一类的同步化(synchronization)机制。但是,此类机制的引入必然会降低程序的并行性(parallelism),因为同步化本身就是串行化(serialization)的一种表现形式。而且,频繁的锁权争夺操作很可能会导致任务无暇去做它们应做的事情,所以如果在程序中使用锁这样的技术来实现同步化,是非常容易出错的。

幸运的是,有一些数据共享技术可以在不降低性能或增加错误率的情况下使用。这些技术包括使用不可变的只读数据、用发送消息的方式取代更新共享变量,以及在算法中引入新的步骤,以便在一些合适的检查点(checkpoint)上合并一些可变的本地变量。可扩展性共享技术可能需要我们修改一些现成的算法。

可扩展的数据共享需要我们对算法进行一些修改。

此外,在面向对象设计中,内存中往往会形成一个复杂而高度连通的对象引用图,这使得传统的面向对象模式很难适用于可扩展的并行执行要求。对此,我们首先想到的方法或许是,在确保多任务共享的情况下,将这些庞大而互相引用的对象图成员视为可变的共享状态,并对这些成员进行外层封装,用锁等方式来强制执行串行化访问。遗憾的是,这并不是一种可扩展的共享方式。锁通常会给所有的内核带来负面的性能影响,它强制内核暂停和通信的动作会消耗一定的时间;它在代码中引入串行部分又会降低并行化的可能性。而随着内核数量的增长,锁带来的开销会使争议愈加强烈。当面对越来越多任务需要共享同一块数据的需求时,关于锁的成本控制将会成为程序性能的主要考量。

引入同步化机制(比如锁)会降低应用程序的可扩展性。

除了性能问题之外,这种依赖于同步机制的程序还会有各种各样的问题,其中就包括死锁(deadlock)。死锁通常是指在两个或者更多的任务之间发生的彼此都在等待对方释放锁的状况。事实上,大部分关于并行编程(www.cppentry.com)的噩梦基本上都起源于错误地使用了可变共享状态(Shared mutable state)或者锁协议(Locking protocol)。

尽管如此,如果我们在对象图(object graph)中适量地使用一些同步化元素,对于编写可扩展的并行程序是有益的。本书也会尽量谨慎地使用同步机制,你在应用中也应该慎重。事实上,你可以把锁看做并行编程(www.cppentry.com)当中的goto语句,尽管它很容易出错,但在某些特定情况下这是必需的,并且对于编译器及其库而言,它可以被视为一种最左派(best left)注3的选择。

没有人会仅仅因为性能因素就将同步机制完全排除在外,除非是正确性需要。毕竟代码本身的正确性才是最重要的。总之,在设计的过程中有限地引入同步机制也是非常重要的设计原则。不要事后才想起往应用程序里加入同步机制。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇1.1 潜在并行化的重要意义 下一篇1.3 选择正确的设计模式

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: