并行编程看似简单,但是其实要设计一个较完美的解决方案是很复杂的。可能由于神秘的错误,程序并不能如预期那样运行。从今天起,我打算每星期更新一次关于并行编程方面的文章。
在Net4.0中引入了一种新的编程模型,极大简化了并行编程的难度和工作。后台使用了高效的登山算法等保证效率。此外,NET4.0中还包含调试和分析的工具,方便我们更好的掌握并行编程。
1:任务(Task)
任务是协同工作的一系列顺序操作,他们共同完成一个更大的操作。我们在构建并行程序时,要确定任务的粒度,这有助于硬件的有效利用。如果选择太小,任务管理的开销占主要,如果太粗,可能会失去并行的机会,因为原本可以使用的内核却被闲置。在一般情况下,我们确定任务时应尽可能的大,但他们之间应该相互独立,并且保证有足够多的任务使内核都忙碌。在调试任务时,可能还需要用到探讨法。要把问题分解为任务,对算法、应用程序的结构要有很好的了解。
2:数据的可扩展共享
不同任务之间通常需要数据共享。问题是,当一个程序并行运行时,程序的不同部分可能会互相竞争对同一内存位置的数据进行更新。这种行为对整个程序来说是灾难性的。解决办法包括同步线程技术。
在特定情况下通过阻塞并发线程的执行来实现并发线程的同步,比如:锁、原子级别的"比较并交换"、信号灯等。对于数据共享,第一反应可能是添加锁或者其它机制,但是降低了程序的并发性能。除了影响性能外,可能还会造成死锁等茶几问题。但是在有节制的情况中使用锁等同步方式是合理的,不能以性能名义忽略同步的必要性。程序在正确运行的基础上才可以思索它的性能问题。任何一种同步形式都是顺序形式。解决这种情况可以使用不可修改的可读数据、限制程序对共享变量的依赖、在逻辑中添加新步骤、在适当的地方合并对共享数据的描述等。
3:并行性能的极限
阿姆达定律(Amdahl)------我大IBM吉恩·阿姆达尔在1967年提出的计算机科学中的一个重要定律。
Amdahl定律定义:
加速比=采用改进措施后性能/未采用改进措施前的性能=未采用改进措施前执行某任务时间/采用改进措施后执行某任务的时间n个处理器加速因子。
S=n/[1+(n-1)f]:f为非平行百分比,n越大,S不能超过1/f。看着很牛B的样子,其实一言以蔽之:不管有多少内核,并行程序能获得的最大加速比是(1/顺序处理所花费
的时间,理论上)。它保证了你在使用并行编程时对性能的最大期望值。
4:一些经验
1):尽量停留在抽象最高层使用结构块或库去做并行。
2):使用已有的并行,比如iis、数据库中的并行性。
3):使用API封装并行性,比如尽可能使用TPL/PLinq。
4):在保证整体架构稳妥的前提上再去思考并行性。
5):尽可能少使用锁、尽量避免共享数据。如果要用共享数据,可能使用共享队列等API。