14.1.1 并行以隔离命令式代码

14.1.1 并行以隔离命令式代码

在命令式编程中,可以很容易并行化的最常见结构是 for 循环。当循环的迭代独立(independent)时,我们可以在单独的线程上执行。由于独立,就是说,没有迭代会依靠由任何前面迭代所计算的值。

例如,当对数组中的元素求和时,我们需要所有以前元素的总和去计算下一个。(这仍可以并行,但不是那么简单。)回忆"模糊化"数组的函数,我们在第 10 章中实现的。这非常适合进行并行化:虽然每次迭代使用输入数组的多个元素,但它在输出数组中,不依赖任何元素。清单 14.1 显示了一个简单的 for 循环,基于前面的示例,包含 C# 和 F# 两个版本。

Listing 14.1 for loop for calculating blurred array (C# and F#)

C#

F#

for(int i=1; i<inp.Length-1; i++){
var sum = inp[i-1] +
inp[i] + inp[i+1];
res[i] = sum / 3;
}

for i in 1 .. inp.Length - 2 do
let sum = inp.[i-1] +
inp.[i] + inp.[i+1]
res.[i] <- sum / 3

虽然是命令式代码,它仍可以成为纯函数式程序的一部分。Inp 是输入数组,我们代码的任意位置都不修改它,res 是输出数组,它由循环计算后,也不对其进行修改。

要对这个循环并行化,可以使用 Parallel.For 方法。这个类只在 .NET 4.0 上才可用,在命名空间 System.Threading.Tasks 下。Parallel.For 方法取一个 Action<int> 委托参数值,可以使用 lambda 函数提供。F# 中,直接使用这个方法感觉有点大材小用,所以,我们会定义一个简单的函数,使代码更简洁:

let pfor nfrom nto f =
Parallel.For(nfrom, nto + 1, Action<_>(f)) |> ignore

这段代码打包函数 f (其类型为 int -> unit)到委托类型,然后,运行并行的 for 循环。该方法返回循环是否成功完成的信息,我们并不需要,所以,忽略它。注意,我们还在上限上加了 1,因为上限是包容在 F# 的 for 循环中,而不包含在 C# for 循环和 Parallel.For 方法中。清单 14.2 显示前面示例的并行版本。

Listing 14.2 Parallelized for loop (C# and F#)

C#

F#

Parallel.For(1,inp.Length-1,i => {
var sum = inp[i-1] +
inp[i] + inp[i+1];
res[i] = sum / 3;
});

pfor 1 (inp.Length-2) (fun i –>
let sum = inp.[i-1] +
inp.[i] + inp.[i+1]
res.[i] <- sum / 3
)

如你所见,这与原始的顺序版本几乎一样简单。另外,还显示了函数式构造的强大:由于 lambda 函数,唯一要做的事情就是,当要把顺序的 for 循环转换为并行时,使用 Parallel.For 方法(或在 F# 中使用 pfor 函数),而不是内置的语言构造。

注意

Parallel 类除了包含 For 方法外,还包含 ForEach 方法,可用于对在 C# 中的 foreach 构造,或 F# 中的 for …in … do 构造进行并行化。这两个方法都具有可用来自定义迭代的重载。有些重载允许更改步长,用于 For 方法中增加索引,或者停止平行执行(类似于在 C# 循环中的 break)。如果你感觉需要更多一点儿控制,请参考文档,看看是否其中有一个重载可以帮助你。

当处理数组和其他命令式数据结构时,Parallel.For 方法特别有用。在这一章的稍后(第 14.2.5 节),我们将在一个较大的示例应用程序中用到它,会再次以函数方式使用数组。首先,让我们完成概述。我们将要讨论的其它两种技术都是纯函数式的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值