实验五 并行编程
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
3.4 使用 PLINQ 扩展编程模型实现任务并行编程
static void Main(string[] args) {
Stopwatch watch = new Stopwatch(); watch.Start(); int[] src = Enumerable.Range(0, 200).ToArray(); var query = src.AsParallel()
上述代码显示了串行执行(由休眠操作模拟)与并行执行时间的差异。在双核机器上,时间减半 了。想象你通常在 For 循环(或 Foreach 循环)中所做的所有工作,并考虑到您现在可以通过使用新 的 Parallel.For 和 Parallel.Foreach 方法获得的时间增强。
一个重要的注意事项:有时它不是那么容易。 当你的工作并行化时,有时你需要照顾同步和死 锁等特殊问题; 你还需要处理这些,我将在下面的例子中讨论它们。接下来,让我们看看 Parallel.For 方法的重载。为了支持断开并行循环,Parallel.For 方法中有一个重载,它将 ParallelLoopState 作 为 Action 代理中的一个参数: Parallel.For(0, 1000, (int i, ParallelLoopState loopState) => {
3.5 数据竞争
static void Main(string[] args) {
6
Stopwatch watch = new Stopwatch(); watch.Start(); Parallel.For(0, 100000, i => {
Thread.Sleep(1);
counter++; }); watch.Stop(); Console.WriteLine("Seconds Elapsed: " + watch.Elapsed.Seconds); Console.WriteLine(counter.ToString()); Console.ReadKey(); }
//LINQ to Objects watch = new Stopwatch(); watch.Start();
4
bool[] results1 = arr.Select(x => IsPrime(x)) .ToArray();
watch.Stop(); Console.WriteLine("LINQ took: " + watch.Elapsed.Seconds);
现在,编辑代码并使用 AsOrdered 扩展方法,如下:
var query = src.AsParallel().AsOrdered() .Select(x => Expensive(x));
再次运行示例,此时注意输出从 0 到 199。这一次,AsOrdered 扩展强制使用缓冲区, 其中所有工作单元的输出在被刷新回最终输出之前被收集和排列。下图显示了以下过程:
上面的代码使用 Parallel.For 方法来执行一个简单的 Sleep 语句。每次循环体被执行时, 计数器都会增加。如果您在多核机器上运行程序,您希望看到什么数字? 从逻辑上来说是 100000。现在,运行程序,你会注意到这个数字少于(如果你得到 10 万,你只是幸运,所 以再试一次)。那么这里发生了什么?为什么当它看起来很明显时我们得到这个错误呢?答 案是数据竞争。我们来解释一下这里的诀窍在于理解语句 counter ++如何被执行。当在 counter ++编译成中间语言(IL)时,其不再是单一的语句。实际上,它由 JIT(即时编译器) 在分 4 步骤执行:
Stopwatch watch;
//for loop watch = new Stopwatch(); watch.Start(); bool[] results = new bool[arr.Length]; for (int i = 0; i < arr.Length; i++) {
results[i] = IsPrime(arr[i]); } watch.Stop(); Console.WriteLine("For Loop took: " + watch.Elapsed.Seconds);
.Select(x => Expensive(x));
foreach (var x in query) {
Console.WriteLine(x); } watch.Stop(); Console.WriteLine("Elapsed: " + watch.Elapsed.Seconds.ToString());
#region 注释块 2
//t2.ContinueWith(delegate
// {
//
Console.WriteLine("Here i am");
// });
//Console.WriteLine("Waiting my task");
2
#endregion
Console.ReadLine(); }
if (i == 500) loopState.Break();
//or loopState.Stop(); return; // to ensure that this iteration also returns
//do stuff });
3.3 使用 PLINQ 编程模型实现任务并行编程
static void Main(string[] args) {
var t2 = t.ContinueWith(delegate {
//simulate compute intensive Thread.Sleep(5000); return "Tasks Example"; });
#region 注释块 1 //string result = t2.Result; //Console.WriteLine("result of second task is: " + result); #endregion
在上面的代码中,我们首先使用 Task 类创建一个新的 Task,在该 Task 中运行一个 lambda 表达式函数(在一个单独的线程中)。
另一个特点是能够在一个正在运行的任务上“继续”。这意味着一旦完成任务(任务 t), 就开始运行另一个任务 t2,并以匿名方法实现。
现在取消注释块 1。在这里,我们看到另一个功能是从任务获取结果。请注意,我们通 过“return”语句任务 2 赋值“Tasks Example”。现在在块 1 中,我们可以检索该值。这会引 发一个问题:如果在 t2 完成执行之前执行语句 t2.Result 会发生什么(回想 t2 在与主程序不 同的线程上运行)?强制这个行为的发生(这就是为什么我使用 Thread.Sleep),你会看到在 这种情况下,主线程将等待 t2.Result,直到 t2 完成执行并执行 return 语句。
//PLINQ watch = new Stopwatch(); watch.Start(); bool[] results2 = arr.AsParallel().Select(x => IsPrime(x))
.ToArray(); watch.Stop(); Console.WriteLine("PLINQ took: " + watch.Elapsed.Seconds);
3.2 使用 Parallel 编程模型实现任务并行编程
static void Main(string[] args) {
Stopwatch watch; watch = new Stopwatch(); watch.Start();
//serial implementation for (int i = 0; i < 10; i++) {
为利用并行计算,通常计算问题表现为以下特征: 1)将工作分离成离散部分,有助于同时解决; 2)随时并及时地执行多个程序指令; 3)多计算资源下解决问题的耗时要少于单个计算资源下的耗时。 并行计算是相对于串行计算来说的,所谓并行计算分为时间上的并行和空间上的并行。 时间上的并行就是指流水线技术,而空间上的并行则是指用多个处理器并发的执行计算. 2. .NET Framework 4 中的并行编程体系
Console.ReadLine(); }
上面的代码显示了如何使用 LINQ(和 for)与 PLINQ 执行 IsPrime 函数。 AsParallel 扩 展允许您的代码使用 PLINQ 而不是 LINQ 运行。 使用 AsParallel 的步骤将数据源与 ParallelQuery 包装器相结合,并使查询中的其余扩展方法绑定到 PLINQ 而不是 LINQ to Objects。运行示例,您将注意到,使用串行 For 循环和传统 LINQ 执行的时间几乎相同, 使 用 PLINQ 的执行时间几乎削减了一半。
实验五 并行编程
一、实验目的
了解.Net 并行编程的机制; 掌握并行编程的基本原理和方法; 使用并行方法实现简单的并行程序设计与开发。
二、实验内容
1. 并行计算概念
并行计算(Parallel Computing)是指同时使用多种计算资源解决计算问题的过程。为执 行并行计算,计算资源应包括一台配有多处理机(并行处理)的计算机、一个与网络相连的 计算机专有编号,或者两者结合使用。并行计算的主要目的是快速解决大型且复杂的计算问 题。此外还包括:利用非本地资源,节约成本 ― 使用多个“廉价”计算资源取代大型计算 机,同时克服单个计算机上存在的存储器限制.
Console.ReadLine(); }
private static int Expensive源自文库int x) {
Thread.Sleep(1); 5
return x; }
这段代码与前面的例子相似。 它显示了如何使用 PLINQ 的 AsParallel 扩展来执行一个 函数。 现在运行程序。 你会看到的是你可能没有想到的。 请注意,打印 0 到 199 的值; 但 是,它们没有以正确的顺序打印。考虑一下:我们使用的是从 0 到 200 的枚举器。但是,由 于我们使用 PLINQ 而不是 LINQ,所以区间本身不是串行的。程序运行过程分为随机并行 线程执行; 例如,从 1、6 分配给 Thread 1,而 3、8 分配给 Thread 2,等等。这就是为什么 输出不是顺序的。下图显示了这一点:
Thread.Sleep(1000); //Do stuff } watch.Stop(); Console.WriteLine("Serial Time: " + watch.Elapsed.Seconds.ToString());
//parallel implementation watch = new Stopwatch(); watch.Start(); Parallel.For(0, 10, i => {
Thread.Sleep(1000); //Do stuff with i
3
}); watch.Stop(); Console.WriteLine("Parallel Time: " + watch.Elapsed.Seconds.ToString());
Console.ReadLine(); }
现在取消注释块 2。在第一个和第二个 Console.WriteLine 语句中设置两个断点。运行程 序,并注意到“Waiting my task”行在“Here I am”之前打印。这表明主程序(线程)正在 等待在另一个线程上运行的任务 t2 完成。一旦 t2 完成(使用 Thread.Sleep 延迟),则会打印 其语句。
1
3、并行编程实现
3.1 使用 Task 编程模型实现任务并行编程
static void Main(string[] args) {
Task t = Task.Factory.StartNew(() => {
Console.WriteLine("I am the first task"); });
许多个人计算机和工作站都有两个或四个内核(即 CPU),使多个线程能够同时执行。 在不久的将来,计算机预期会有更多的内核。 为了利用当今和未来的硬件,您可以对代码 进行并行化,以将工作分摊在多个处理器上。 过去,并行化需要线程和锁的低级操作。
Visual Studio 2010 和 .NET Framework 4 提供了新的运行时、新的类库类型以及新的 诊断工具,从而增强了对并行编程的支持。 这些功能简化了并行开发,使您能够通过固有 方法编写高效、细化且可伸缩的并行代码,而不必直接处理线程或线程池。 下图从较高层 面上概述了 .NET Framework 4 中的并行编程体系结构。
static void Main(string[] args) {
Stopwatch watch = new Stopwatch(); watch.Start(); int[] src = Enumerable.Range(0, 200).ToArray(); var query = src.AsParallel()
上述代码显示了串行执行(由休眠操作模拟)与并行执行时间的差异。在双核机器上,时间减半 了。想象你通常在 For 循环(或 Foreach 循环)中所做的所有工作,并考虑到您现在可以通过使用新 的 Parallel.For 和 Parallel.Foreach 方法获得的时间增强。
一个重要的注意事项:有时它不是那么容易。 当你的工作并行化时,有时你需要照顾同步和死 锁等特殊问题; 你还需要处理这些,我将在下面的例子中讨论它们。接下来,让我们看看 Parallel.For 方法的重载。为了支持断开并行循环,Parallel.For 方法中有一个重载,它将 ParallelLoopState 作 为 Action 代理中的一个参数: Parallel.For(0, 1000, (int i, ParallelLoopState loopState) => {
3.5 数据竞争
static void Main(string[] args) {
6
Stopwatch watch = new Stopwatch(); watch.Start(); Parallel.For(0, 100000, i => {
Thread.Sleep(1);
counter++; }); watch.Stop(); Console.WriteLine("Seconds Elapsed: " + watch.Elapsed.Seconds); Console.WriteLine(counter.ToString()); Console.ReadKey(); }
//LINQ to Objects watch = new Stopwatch(); watch.Start();
4
bool[] results1 = arr.Select(x => IsPrime(x)) .ToArray();
watch.Stop(); Console.WriteLine("LINQ took: " + watch.Elapsed.Seconds);
现在,编辑代码并使用 AsOrdered 扩展方法,如下:
var query = src.AsParallel().AsOrdered() .Select(x => Expensive(x));
再次运行示例,此时注意输出从 0 到 199。这一次,AsOrdered 扩展强制使用缓冲区, 其中所有工作单元的输出在被刷新回最终输出之前被收集和排列。下图显示了以下过程:
上面的代码使用 Parallel.For 方法来执行一个简单的 Sleep 语句。每次循环体被执行时, 计数器都会增加。如果您在多核机器上运行程序,您希望看到什么数字? 从逻辑上来说是 100000。现在,运行程序,你会注意到这个数字少于(如果你得到 10 万,你只是幸运,所 以再试一次)。那么这里发生了什么?为什么当它看起来很明显时我们得到这个错误呢?答 案是数据竞争。我们来解释一下这里的诀窍在于理解语句 counter ++如何被执行。当在 counter ++编译成中间语言(IL)时,其不再是单一的语句。实际上,它由 JIT(即时编译器) 在分 4 步骤执行:
Stopwatch watch;
//for loop watch = new Stopwatch(); watch.Start(); bool[] results = new bool[arr.Length]; for (int i = 0; i < arr.Length; i++) {
results[i] = IsPrime(arr[i]); } watch.Stop(); Console.WriteLine("For Loop took: " + watch.Elapsed.Seconds);
.Select(x => Expensive(x));
foreach (var x in query) {
Console.WriteLine(x); } watch.Stop(); Console.WriteLine("Elapsed: " + watch.Elapsed.Seconds.ToString());
#region 注释块 2
//t2.ContinueWith(delegate
// {
//
Console.WriteLine("Here i am");
// });
//Console.WriteLine("Waiting my task");
2
#endregion
Console.ReadLine(); }
if (i == 500) loopState.Break();
//or loopState.Stop(); return; // to ensure that this iteration also returns
//do stuff });
3.3 使用 PLINQ 编程模型实现任务并行编程
static void Main(string[] args) {
var t2 = t.ContinueWith(delegate {
//simulate compute intensive Thread.Sleep(5000); return "Tasks Example"; });
#region 注释块 1 //string result = t2.Result; //Console.WriteLine("result of second task is: " + result); #endregion
在上面的代码中,我们首先使用 Task 类创建一个新的 Task,在该 Task 中运行一个 lambda 表达式函数(在一个单独的线程中)。
另一个特点是能够在一个正在运行的任务上“继续”。这意味着一旦完成任务(任务 t), 就开始运行另一个任务 t2,并以匿名方法实现。
现在取消注释块 1。在这里,我们看到另一个功能是从任务获取结果。请注意,我们通 过“return”语句任务 2 赋值“Tasks Example”。现在在块 1 中,我们可以检索该值。这会引 发一个问题:如果在 t2 完成执行之前执行语句 t2.Result 会发生什么(回想 t2 在与主程序不 同的线程上运行)?强制这个行为的发生(这就是为什么我使用 Thread.Sleep),你会看到在 这种情况下,主线程将等待 t2.Result,直到 t2 完成执行并执行 return 语句。
//PLINQ watch = new Stopwatch(); watch.Start(); bool[] results2 = arr.AsParallel().Select(x => IsPrime(x))
.ToArray(); watch.Stop(); Console.WriteLine("PLINQ took: " + watch.Elapsed.Seconds);
3.2 使用 Parallel 编程模型实现任务并行编程
static void Main(string[] args) {
Stopwatch watch; watch = new Stopwatch(); watch.Start();
//serial implementation for (int i = 0; i < 10; i++) {
为利用并行计算,通常计算问题表现为以下特征: 1)将工作分离成离散部分,有助于同时解决; 2)随时并及时地执行多个程序指令; 3)多计算资源下解决问题的耗时要少于单个计算资源下的耗时。 并行计算是相对于串行计算来说的,所谓并行计算分为时间上的并行和空间上的并行。 时间上的并行就是指流水线技术,而空间上的并行则是指用多个处理器并发的执行计算. 2. .NET Framework 4 中的并行编程体系
Console.ReadLine(); }
上面的代码显示了如何使用 LINQ(和 for)与 PLINQ 执行 IsPrime 函数。 AsParallel 扩 展允许您的代码使用 PLINQ 而不是 LINQ 运行。 使用 AsParallel 的步骤将数据源与 ParallelQuery 包装器相结合,并使查询中的其余扩展方法绑定到 PLINQ 而不是 LINQ to Objects。运行示例,您将注意到,使用串行 For 循环和传统 LINQ 执行的时间几乎相同, 使 用 PLINQ 的执行时间几乎削减了一半。
实验五 并行编程
一、实验目的
了解.Net 并行编程的机制; 掌握并行编程的基本原理和方法; 使用并行方法实现简单的并行程序设计与开发。
二、实验内容
1. 并行计算概念
并行计算(Parallel Computing)是指同时使用多种计算资源解决计算问题的过程。为执 行并行计算,计算资源应包括一台配有多处理机(并行处理)的计算机、一个与网络相连的 计算机专有编号,或者两者结合使用。并行计算的主要目的是快速解决大型且复杂的计算问 题。此外还包括:利用非本地资源,节约成本 ― 使用多个“廉价”计算资源取代大型计算 机,同时克服单个计算机上存在的存储器限制.
Console.ReadLine(); }
private static int Expensive源自文库int x) {
Thread.Sleep(1); 5
return x; }
这段代码与前面的例子相似。 它显示了如何使用 PLINQ 的 AsParallel 扩展来执行一个 函数。 现在运行程序。 你会看到的是你可能没有想到的。 请注意,打印 0 到 199 的值; 但 是,它们没有以正确的顺序打印。考虑一下:我们使用的是从 0 到 200 的枚举器。但是,由 于我们使用 PLINQ 而不是 LINQ,所以区间本身不是串行的。程序运行过程分为随机并行 线程执行; 例如,从 1、6 分配给 Thread 1,而 3、8 分配给 Thread 2,等等。这就是为什么 输出不是顺序的。下图显示了这一点:
Thread.Sleep(1000); //Do stuff } watch.Stop(); Console.WriteLine("Serial Time: " + watch.Elapsed.Seconds.ToString());
//parallel implementation watch = new Stopwatch(); watch.Start(); Parallel.For(0, 10, i => {
Thread.Sleep(1000); //Do stuff with i
3
}); watch.Stop(); Console.WriteLine("Parallel Time: " + watch.Elapsed.Seconds.ToString());
Console.ReadLine(); }
现在取消注释块 2。在第一个和第二个 Console.WriteLine 语句中设置两个断点。运行程 序,并注意到“Waiting my task”行在“Here I am”之前打印。这表明主程序(线程)正在 等待在另一个线程上运行的任务 t2 完成。一旦 t2 完成(使用 Thread.Sleep 延迟),则会打印 其语句。
1
3、并行编程实现
3.1 使用 Task 编程模型实现任务并行编程
static void Main(string[] args) {
Task t = Task.Factory.StartNew(() => {
Console.WriteLine("I am the first task"); });
许多个人计算机和工作站都有两个或四个内核(即 CPU),使多个线程能够同时执行。 在不久的将来,计算机预期会有更多的内核。 为了利用当今和未来的硬件,您可以对代码 进行并行化,以将工作分摊在多个处理器上。 过去,并行化需要线程和锁的低级操作。
Visual Studio 2010 和 .NET Framework 4 提供了新的运行时、新的类库类型以及新的 诊断工具,从而增强了对并行编程的支持。 这些功能简化了并行开发,使您能够通过固有 方法编写高效、细化且可伸缩的并行代码,而不必直接处理线程或线程池。 下图从较高层 面上概述了 .NET Framework 4 中的并行编程体系结构。