并行扩展至 .NET 4.0 运行时






3.54/5 (10投票s)
本文主要关注 TPL。
总述
本文将主要关注任务并行库 (Task Parallel Library) - 它的用法及其在并行编程中的地位。为了避免遗漏一些必要的概念,本文还将(简要)提及 PLINQ 和 Parallel 类的 For 方法。微软在其最新的 .NET 4.0 运行时版本中加入了一些非常必要的改进,以跟上多核处理器技术的发展。程序员不仅面临着技术上的微小变化,最终还将不得不适应并行编程中的一些模式。这些新增内容可以分为四大类:
- 一组面向任务的 API,称为任务并行库 (TPL)。提供了一个任务对象模型,用于思考并行编程,此外还有一些辅助类,用于执行常见的命令式(非声明式)数据并行操作,例如并行 For 循环。这些结构和多线程 API,以及 Parallel 类,共同构成了并行框架 (PFX)。
- .NET LINQ 的数据并行实现。Parallel LINQ (PLINQ) 查询提供程序通过 TPL,间接自动并行化对内存数据结构的任何 LINQ-to-Objects 查询。
- 一套完整的同步原语,封装了常见的协调事件。
- 一组并发集合
任务并行库
TPL 中的并发单元是 Task 对象。此类提供了大部分功能,并且像大多数其他 TPL 类一样,可以在 System.Threading.Tasks 命名空间中找到。
public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
{
//Constructors
public Task(Action action);
public Task(Action<Object> action, Object state);
public Task(Action action);
public Task(Action<Object> action, Object state);
public Task(Action action, TaskManager, taskManager);
public Task(Action<Object> action,Object state,TaskCreationOptions creationOptions)
public Task(
Action action,
TaskManager, taskManager,
TaskCreationOptions, options
);
// static factory methods
public static Task StartNew(Action action);
public static Task StartNew(Action<Object> action, Object state);
public static Task StartNew(Action action, Action action;
public static Task StartNew(
Action action,
TaskManager, taskManager
TaskCreationOptions, options
);
public static Task StartNew(
Action<Object> action,
object state,
TaskManager, taskManager
TaskCreationOptions, options
);
// Methods
public void Cancel();
public void CancelAndWait();
public bool CancelAndWait(int millisecondTimeout;
public bool CancelAndWait(TimeSpan timeout);
public Task ContinueWith(Action<Task> action);
public Task ContinueWith(
Action<Task> action,
TaskContinuation kind
);
public Task ContinueWith(
Action<Task> action,
TaskContinuation kind,
TaskCreationOptions, options
);
public Task ContinueWith(
Action<Task> action,
TaskContinuation kind,
TaskCreationOptions, options,
bool executeSynchronosuly
);
public void Dispose();
public void Start();
public void Wait();
public bool Wait(int millisecondTimeout);
public bool Wait(TimeSpan timeout);
public static void WaitAll(params Task[] tasks);
public static bool WaitAll(Task[] tasks, int millisecondTimeout);
public static bool WaitAll(Task[] tasks, TimeSpan timeout);
public static void WaitAny(params Task[] tasks);
public static bool WaitAny(Task[] tasks, int millisecondTimeout);
public static bool WaitAny(Task[] tasks, TimeSpan timeout);
// Properties
public static Task Current { get; }
public static Exception { get; }
public int Id { get; }
public bool IsCanceled { get; }
public bool IsCancellationRequested; { get; }
public bool Iscompleted { get; }
public Task Parent { get; }
public TaskStatus Status { get; }
public TaskCreationOptions TaskCreationOptions { get; }
}
Task 的首要方面是构造函数和静态 StartNew 工厂方法。两者都提供相同的重载;StartNew 方法实际上是构造新任务并立即调用其 Start 方法的常见操作的快捷方式。下面的代码示例直接引用自 MSDN 库。创建 Task 对象后,Start() 会开始任务调度过程。但任何读者可能都在想 Task 是如何创建的。一旦一切准备就绪可以并行化,组成线程的代码指令集就会被分配给任务。当 Task 返回结果时,结果是以 Task 的形式出现的
using System;
using System.Threading;
using System.Threading.Tasks;
class StartNewDemo
{
// Demonstrated features:
// Task ctor()
// Task.Factory
// Task.Wait()
// Task.RunSynchronously()
// Expected results:
// Task t1 (alpha) is created unstarted.
// Task t2 (beta) is created started.
// Task t1's (alpha) start is held until after t2 (beta) is started.
// Both tasks t1 (alpha) and t2 (beta) are potentially executed on threads
// other than the main thread on multi-core machines.
// Task t3 (gamma) is executed synchronously on the main thread.
static void Main()
{
Action<object> action = (object obj) =>
{
Console.WriteLine("Task={0}, obj={1}, Thread={2}",
Task.CurrentId, obj.ToString(), Thread.CurrentThread.ManagedThreadId);
};
// Construct an unstarted task
Task t1 = new Task(action, "alpha");
// Construct a started task
Task t2 = Task.Factory.StartNew(action, "beta");
// Block the main thread to demonstrate that t2 is executing
t2.Wait();
// Launch t1
t1.Start();
Console.WriteLine("t1 has been launched. (Main Thread={0})",
Thread.CurrentThread.ManagedThreadId);
// Wait for the task to finish.
// You may optionally provide a timeout interval or a cancellation token
// to mitigate situations when the task takes too long to finish.
t1.Wait();
// Construct an unstarted task
Task t3 = new Task(action, "gamma");
// Run it synchronously
t3.RunSynchronously();
// Although the task was run synchronously,
// it is a good practice to wait for it which observes for
// exceptions potentially thrown by that task.
t3.Wait();
}
}
输出
Task=1, obj=beta, Thread=3
t1 has been launched. (Main Thread=1)
Task=2, obj=alpha, Thread=3
Task=3, obj=gamma, Thread=1
好的。现在注意到
- 每一个新任务都需要提供一个 Action。这是一个在任务实际运行时将执行的委托。
- 可以选择提供一个对象状态参数。这适用于那些接受 Action<object> 的重载,而该值当然会作为整体传递给委托。回想一下,通过委托进行的方法调用将方法调用的定义和调用分开为两部分。
- 可以提供一个 TaskManager 对象。
当您想在线程池线程上运行某些操作,并通过 continuation 和父/子任务来管理任务的工作流时,任务并行构造非常有用。当您想并行执行操作然后等待它们完成(结构化并行)时,PLINQ 和 Parallel 类非常有用。这包括非 CPU 密集型任务,例如调用 Web 服务。
Parallel For 和 Parallel ForEach
Parallel.For 和 Parallel.ForEach 执行与 C# 的 for 和 foreach 循环等效的操作,但每个迭代都并行执行,而不是顺序执行。这是它们(最简单)的签名:
public static ParallelLoopResult For (
int fromInclusive, int toExclusive, Action<int> body)
public static ParallelLoopResult ForEach<TSource> (
IEnumerable<TSource> source, Action<TSource> body)
我们的方法实现将针对范围 [inclusiveLowerBound,exclusiveUpperBound) 中的每个元素调用循环体一次。以下顺序循环
for (int I = 0; I < 100; i++)
Foo();
将被这样并行化:
Parallel.For (0, 100, i => Foo (i));
首先,我们需要一个签名。为了并行化 for 循环,我们将实现一个接受三个参数的方法:一个下界,一个上界,以及一个接受一个整数参数(代表当前迭代索引)的循环体委托(该委托将为每次迭代调用一次)。请注意迭代空间的定义。现在简要看一下这个 PLINQ 示例:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program {
public static void Main() {
IEnumerable<int> numbers = Enumerable.Range (3, 100000-3);
var parallelQuery =
from n in numbers.AsParallel()
where Enumerable.Range (2, (int) Math.Sqrt (n)).All (i => n % i > 0)
select n;
int[] primes = parallelQuery.ToArray();
foreach ( var e in parallelQuery)
{
Console.WriteLine(e);
}
}
}
这会输出到:
3
5
7
11
13
17
19
23
29
31
37
41
43
47
53
……. and so on …
99667
99679
99689
99829
99833
99839
99859
99871
99877
99881
99901
99907
99923
99929
并行 LINQ
LINQ 允许我们通过对 System.Linq.Enumerable 类的 API 调用系列,或者通过使用 C# 等语言支持的语言集成查询语法来编写声明式(而非命令式)查询。枚举器的通用概念是一个类型,其唯一目的是逐个读取并遍历另一个集合的内容。通常没有写入功能。PLINQ 通过分析查询,并安排将查询的不同部分在多个处理器上并行运行。它最终是通过在底层使用 TPL 来实现的。下面的示例引用了一个 Employee 类。实际程序包含一系列私有的静态方法,所有这些方法都用于举例说明并行构造、PLINQ、For 方法循环等的用法。请注意,此类是为了传递给另一个类等而编写的。
using System;
using System.Collections.Generic;
public class Employee
{
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
public string Address
{
get;
set;
}
public DateTime HireDate
{
get;
set;
}
public int EmployeeID
{
get;
set;
}
}
public class EmployeeList : List<Employee>
{
public EmployeeList()
{
Add(new Employee { EmployeeID = 1, FirstName = "Jay",
LastName = "Adams", HireDate = DateTime.Parse("2007/1/1") });
Add(new Employee { EmployeeID = 2, FirstName = "Adam",
LastName = "Barr", HireDate = DateTime.Parse("2006/3/15") });
Add(new Employee { EmployeeID = 3, FirstName = "Karen",
LastName = "Berge", HireDate = DateTime.Parse("2005/6/17") });
Add(new Employee { EmployeeID = 4, FirstName = "Scott",
LastName = "Bishop", HireDate = DateTime.Parse("2000/3/19") });
Add(new Employee { EmployeeID = 5, FirstName = "Jo",
LastName = "Brown", HireDate = DateTime.Parse("2003/7/17") });
Add(new Employee { EmployeeID = 6, FirstName = "David",
LastName = "Campbell", HireDate = DateTime.Parse("2005/9/13") });
Add(new Employee { EmployeeID = 7, FirstName = "Rob",
LastName = "Caron", HireDate = DateTime.Parse("2002/12/3") });
Add(new Employee { EmployeeID = 8, FirstName = "Jane",
LastName = "Clayton", HireDate = DateTime.Parse("2008/7/1") });
Add(new Employee { EmployeeID = 9, FirstName = "Pat",
LastName = "Coleman", HireDate = DateTime.Parse("2008/1/7") });
Add(new Employee { EmployeeID = 10, FirstName = "Aaron",
LastName = "Con", HireDate = DateTime.Parse("2001/11/1") });
Add(new Employee { EmployeeID = 11, FirstName = "Don",
LastName = "Hall", HireDate = DateTime.Parse("2006/4/21") });
Add(new Employee { EmployeeID = 12, FirstName = "Joe",
LastName = "Howard", HireDate = DateTime.Parse("2006/7/19") });
Add(new Employee { EmployeeID = 13, FirstName = "Jim",
LastName = "Kim", HireDate = DateTime.Parse("2001/3/9") });
Add(new Employee { EmployeeID = 14, FirstName = "Eric",
LastName = "Lang", HireDate = DateTime.Parse("2005/7/15") });
Add(new Employee { EmployeeID = 15, FirstName = "Jose",
LastName = "Lugo", HireDate = DateTime.Parse("2003/8/6") });
Add(new Employee { EmployeeID = 16, FirstName = "Nikki",
LastName = "McCormick", HireDate = DateTime.Parse("2005/5/18") });
Add(new Employee { EmployeeID = 17, FirstName = "Susan",
LastName = "Metters", HireDate = DateTime.Parse("2002/8/5") });
Add(new Employee { EmployeeID = 18, FirstName = "Linda",
LastName = "MIctchell", HireDate = DateTime.Parse("2006/10/1") });
Add(new Employee { EmployeeID = 19, FirstName = "Kim",
LastName = "Ralls", HireDate = DateTime.Parse("2002/12/7") });
Add(new Employee { EmployeeID = 20, FirstName = "Jeff",
LastName = "Smith", HireDate = DateTime.Parse("2001/3/30") });
}
}
public class EmployeeHierarchy : Tree<Employee>
{
public EmployeeHierarchy()
{
//root
Data = new Employee { EmployeeID = 1, FirstName = "Jay",
LastName = "Adams", HireDate = DateTime.Parse("2007/1/1") };
//1st level
Left = new Tree<Employee>();
Right = new Tree<Employee>();
Left.Data = new Employee { EmployeeID = 2, FirstName = "Adam",
LastName = "Barr", HireDate = DateTime.Parse("2006/3/15") };
Right.Data = new Employee { EmployeeID = 17, FirstName = "Karen",
LastName = "Berge", HireDate = DateTime.Parse("2005/6/17") };
//2nd level
//left
Left.Left = new Tree<Employee>();
Left.Right = new Tree<Employee>();
Left.Left.Data = new Employee { EmployeeID = 3, FirstName = "Scott",
LastName = "Bishop", HireDate = DateTime.Parse("2000/3/19") };
Left.Right.Data = new Employee { EmployeeID = 14, FirstName = "Jo",
LastName = "Brown", HireDate = DateTime.Parse("2003/7/17") };
//right
Right.Left = new Tree<Employee>();
Right.Right = new Tree<Employee>();
Right.Left.Data = new Employee { EmployeeID = 18, FirstName = "David",
LastName = "Campbell", HireDate = DateTime.Parse("2005/9/13") };
Right.Right.Data = new Employee { EmployeeID = 19, FirstName = "Rob",
LastName = "Caron", HireDate = DateTime.Parse("2002/12/3") };
//3rd level
//left
//left.left
Left.Left.Left = new Tree<Employee>();
Left.Left.Right = new Tree<Employee>();
Left.Left.Left.Data = new Employee { EmployeeID = 4, FirstName = "Jane",
LastName = "Clayton", HireDate = DateTime.Parse("2008/7/1") };
Left.Left.Right.Data = new Employee { EmployeeID = 7, FirstName = "Pat",
LastName = "Coleman", HireDate = DateTime.Parse("2008/1/7") };
//left.right
Left.Right.Left = new Tree<Employee>();
Left.Right.Right = new Tree<Employee>();
Left.Right.Left.Data = new Employee { EmployeeID = 15, FirstName = "Aaron",
LastName = "Con", HireDate = DateTime.Parse("2001/11/1") };
Left.Right.Right.Data = new Employee { EmployeeID = 16, FirstName = "Don",
LastName = "Hall", HireDate = DateTime.Parse("2006/4/21") };
//4th level
//left.left.left
Left.Left.Left.Left = new Tree<Employee>();
Left.Left.Left.Right = new Tree<Employee>();
Left.Left.Left.Left.Data = new Employee { EmployeeID = 5, FirstName = "Joe",
LastName = "Howard", HireDate = DateTime.Parse("2006/7/19") };
Left.Left.Left.Right.Data = new Employee { EmployeeID = 6, FirstName = "Jim",
LastName = "Kim", HireDate = DateTime.Parse("2001/3/9") };
//left.left.right
Left.Left.Right.Left = new Tree<Employee>();
Left.Left.Right.Right = new Tree<Employee>();
Left.Left.Right.Left.Data = new Employee { EmployeeID = 8, FirstName = "Eric",
LastName = "Lang", HireDate = DateTime.Parse("2005/7/15") };
Left.Left.Right.Right.Data = new Employee { EmployeeID = 11, FirstName = "Jose",
LastName = "Lugo", HireDate = DateTime.Parse("2003/8/6") };
Left.Left.Right.Left.Left = new Tree<Employee>();
Left.Left.Right.Left.Right = new Tree<Employee>();
Left.Left.Right.Left.Left.Data = new Employee { EmployeeID = 9, FirstName = "Nikki",
LastName = "McCormick", HireDate = DateTime.Parse("2005/5/18") };
Left.Left.Right.Left.Right.Data = new Employee { EmployeeID = 10, FirstName = "Susan",
LastName = "Metters", HireDate = DateTime.Parse("2002/8/5") };
Left.Left.Right.Right.Left = new Tree<Employee>();
Left.Left.Right.Right.Right = new Tree<Employee>();
Left.Left.Right.Right.Left.Data = new Employee { EmployeeID = 12, FirstName = "Linda",
LastName = "MIctchell", HireDate = DateTime.Parse("2006/10/1") };
Left.Left.Right.Right.Right.Data = new Employee { EmployeeID = 13, FirstName = "Kim",
LastName = "Ralls", HireDate = DateTime.Parse("2002/12/7") };
}
}
public class Tree<T>
{
public T Data;
public Tree<T> Left, Right;
}
public static class PayrollServices
{
public static decimal GetPayrollDeduction(Employee employee)
{
Console.WriteLine("Executing GetPayrollDeduction for employee {0}", employee.EmployeeID);
var rand = new Random(DateTime.Now.Millisecond);
var delay = rand.Next(1, 5);
var count = 0;
var process = true;
while(process)
{
System.Threading.Thread.Sleep(1000);
count++;
if (count >= delay)
process = false;
}
return delay;
}
public static string GetEmployeeInfo(Employee employee)
{
Console.WriteLine("Executing GetPayrollDeduction for employee {0}", employee.EmployeeID);
//Random rand = new Random(System.DateTime.Now.Millisecond);
const int delay = 5;
var count = 0;
var process = true;
while (process)
{
System.Threading.Thread.Sleep(1000);
count++;
if (count >= delay)
process = false;
}
return string.Format("{0} {1}, {2}", employee.EmployeeID,
employee.LastName, employee.FirstName);
}
}
csc /t:Library EmployeeList.cs 输出 EmployeeList.dll。
现在这是外部引用该 DLL 的代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static EmployeeList employeeData;
static void Main(string[] args)
{
employeeData = new EmployeeList();
Console.WriteLine("Payroll process started at {0}", DateTime.Now);
var sw = Stopwatch.StartNew();
// Methods to call
// Ex1Task1_ParallelizeLongRunningService();
// Ex1Task1_UseParallelForMethod();
// Ex1Task1_StandardForEach();
// Ex1Task1_ParallelForEach();
// Ex1Task1_WalkTree();
// Ex2Task1_NativeParallelTasks();
// Ex2Task2_WaitHandling();
// Ex2Task2_WaitHandlingWaitAll();
// Ex2Task3_TaskIsCompleted();
// Ex2Task4_ContinueWith();
// Ex3Task1_TaskReturnValue();
// Ex4Task1_PLINQ();
// Ex4Task1_PLINQAsParallel();
// Ex4Task2_Extensions();
// Ex4Task2_ConvertToParallelExtensions();
Ex4Task3_PLINQComprehensionSyntax();
Console.WriteLine("Payroll finished at {0} and took {1}",
DateTime.Now, sw.Elapsed.TotalSeconds);
Console.WriteLine();
Console.ReadLine();
}
private static void Ex1Task1_ParallelizeLongRunningService()
{
Console.WriteLine("Non-parallelized for loop");
for (int i = 0; i < employeeData.Count; i++)
{
Console.WriteLine("Starting process for employee id {0}",
employeeData[i].EmployeeID);
decimal span =
PayrollServices.GetPayrollDeduction(employeeData[i]);
Console.WriteLine("Completed process for employee id {0}" +
"process took {1} seconds",
employeeData[i].EmployeeID, span);
Console.WriteLine();
}
}
private static void Ex1Task1_UseParallelForMethod()
{
Parallel.For(0, employeeData.Count, i =>
{
Console.WriteLine("Starting process for employee id {0}",
employeeData[i].EmployeeID);
decimal span =
PayrollServices.GetPayrollDeduction(employeeData[i]);
Console.WriteLine("Completed process for employee id {0}",
employeeData[i].EmployeeID);
Console.WriteLine();
});
}
private static void Ex1Task1_StandardForEach()
{
foreach (Employee employee in employeeData)
{
Console.WriteLine("Starting process for employee id {0}",
employee.EmployeeID);
decimal span =
PayrollServices.GetPayrollDeduction(employee);
Console.WriteLine("Completed process for employee id {0}",
employee.EmployeeID);
Console.WriteLine();
}
}
private static void Ex1Task1_ParallelForEach()
{
Parallel.ForEach(employeeData, ed =>
{
Console.WriteLine("Starting process for employee id {0}",
ed.EmployeeID);
decimal span = PayrollServices.GetPayrollDeduction(ed);
Console.WriteLine("Completed process for employee id {0}",
ed.EmployeeID);
Console.WriteLine();
});
}
private static void Ex1Task1_WalkTree()
{
EmployeeHierarchy employeeHierarchy = new EmployeeHierarchy();
WalkTree(employeeHierarchy);
}
private static void WalkTree(Tree<employee> node)
{
if (node == null)
return;
if (node.Data != null)
{
Employee emp = node.Data;
Console.WriteLine("Starting process for employee id {0}",
emp.EmployeeID);
decimal span = PayrollServices.GetPayrollDeduction(emp);
Console.WriteLine("Completed process for employee id {0}",
emp.EmployeeID);
Console.WriteLine();
}
Parallel.Invoke(delegate { WalkTree(node.Left); },
delegate { WalkTree(node.Right); });
}
private static void Ex2Task1_NativeParallelTasks()
{
Task task1 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[0]); });
Task task2 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[1]); });
Task task3 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[2]); });
}
private static void Ex2Task2_WaitHandling()
{
Task task1 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[0]); });
Task task2 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[1]); });
Task task3 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[2]); });
task1.Wait();
task2.Wait();
task3.Wait();
}
private static void Ex2Task2_WaitHandlingWaitAll()
{
Task task1 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[0]); });
Task task2 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[1]); });
Task task3 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[2]); });
Task.WaitAll(task1, task2, task3);
}
private static void Ex2Task3_TaskIsCompleted()
{
Task task1 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[0]); });
while (!task1.IsCompleted)
{
Thread.Sleep(1000);
Console.WriteLine("Waiting on task 1");
}
Task task2 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[1]); });
while (!task2.IsCompleted)
{
Thread.Sleep(1000);
Console.WriteLine("Waiting on task 2");
}
Task task3 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[2]); });
while (!task3.IsCompleted)
{
Thread.Sleep(1000);
Console.WriteLine("Waiting on task 3");
}
}
private static void Ex2Task4_ContinueWith()
{
Task task3 = Task.Factory.StartNew(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[0]); })
.ContinueWith(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[1]); })
.ContinueWith(delegate
{ PayrollServices.GetPayrollDeduction(employeeData[2]); });
task3.Wait();
}
private static void Ex3Task1_TaskReturnValue()
{
Console.WriteLine("Calling parallel task with return value");
var data = Task.Factory.StartNew(() =>
PayrollServices.GetPayrollDeduction(employeeData[0]));
Console.WriteLine("Parallel task returned with value of {0}",
data.Result);
}
static void Ex4Task1_PLINQ()
{
var q = Enumerable.Select(
Enumerable.OrderBy(
Enumerable.Where(employeeData,
x => x.EmployeeID % 2 == 0),
x => x.EmployeeID),
x => PayrollServices.GetEmployeeInfo(x))
.ToList();
foreach (var e in q)
{
Console.WriteLine(e);
}
}
static void Ex4Task1_PLINQAsParallel()
{
var q = ParallelEnumerable.Select(
ParallelEnumerable.OrderBy(
ParallelEnumerable.Where(employeeData.AsParallel(),
x => x.EmployeeID % 2 == 0),
x => x.EmployeeID),
x => PayrollServices.GetEmployeeInfo(x))
.ToList();
foreach (var e in q)
{
Console.WriteLine(e);
}
}
private static void Ex4Task2_Extensions()
{
var q = employeeData.
Where(x => x.EmployeeID % 2 == 0).OrderBy(x => x.EmployeeID)
.Select(x => PayrollServices.GetEmployeeInfo(x))
.ToList();
foreach (var e in q)
{
Console.WriteLine(e);
}
}
private static void Ex4Task2_ConvertToParallelExtensions()
{
var q = employeeData.AsParallel()
.Where(x => x.EmployeeID % 2 == 0).OrderBy(x => x.EmployeeID)
.Select(x => PayrollServices.GetEmployeeInfo(x))
.ToList();
foreach (var e in q)
{
Console.WriteLine(e);
}
}
private static void Ex4Task3_PLINQComprehensionSyntax()
{
var q = from e in employeeData.AsParallel()
where e.EmployeeID % 2 == 0
orderby e.EmployeeID
select PayrollServices.GetEmployeeInfo(e);
foreach (var e in q)
{
Console.WriteLine(e);
}
}
}
csc /r:EmployeeList.dll Program.cs 输出:
Payroll process started at 4/12/2010 10:43:45 PM
Executing GetPayrollDeduction for employee 12
Executing GetPayrollDeduction for employee 2
Executing GetPayrollDeduction for employee 14
Executing GetPayrollDeduction for employee 4
Executing GetPayrollDeduction for employee 6
Executing GetPayrollDeduction for employee 16
Executing GetPayrollDeduction for employee 8
Executing GetPayrollDeduction for employee 18
Executing GetPayrollDeduction for employee 20
Executing GetPayrollDeduction for employee 10
2 Barr, Adam
4 Bishop, Scott
6 Campbell, David
8 Clayton, Jane
10 Con, Aaron
12 Howard, Joe
14 Lang, Eric
16 McCormick, Nikki
18 MIctchell, Linda
20 Smith, Jeff
Payroll finished at 4/12/2010 10:44:11 PM and took 25.4169016
由于本文开头提到了四个主要类别,而我以我有限的知识,基本上只涵盖了 TPL。并行框架还有很多内容,技术文档坚持认为学习这些概念至关重要。
参考文献
- Stephen Toub 的《并行编程模式》
- MSDN 库
- Joe Duffy 的《Windows 并发编程》
历史
- 2010 年 4 月 12 日:首次发布