- 并行编程
- 任务并行
- 隐式创建和运行任务
- 显式创建和运行任务
- 任务 ID
- 任务创建选项
- 创建任务延续
- 创建分离的子任务
- 创建子任务
- 等待任务完成
- 组合任务
- 任务中的异常处理
- 取消任务
- TaskFactory 类
- 无委托的任务
- 相关数据结构
- 参考资料
下载 Demo
下载 Samples for Parallel Programming with .net framework
并行编程
多核 CPU 已经相当普遍,使得多个线程能够同时执行。将代码并行化,工作也就分摊到多个 CPU 上。
过去,并行化需要线程和锁的低级操作。而 Visual Studio 2010 和 .NET Framework 4 开始提供了新的运行时、新的类库类型以及新的诊断工具,从而增强了对并行编程的支持。这些功能简化了并行开发,通过固有方法编写高效、细化且可伸缩的并行代码,而不必直接处理线程或线程池。
下图从较高层面上概述了 .NET Framework 4 中的并行编程体系结构。
任务并行库(The Task Parallel Library,TPL)是 System.Threading 和 System.Threading.Tasks 空间中的一组公共类型和 API。TPL 的目的是通过简化将并行和并发添加到应用程序的过程来提高开发人员的工作效率。TPL 能动态地最有效地使用所有可用的处理器。此外,TPL 还处理工作分区、ThreadPool 上的线程调度、取消支持、状态管理以及其他低级别的细节操作。通过使用 TPL,你可以将精力集中于程序要完成的工作,同时最大程度地提高代码的性能。
从 .NET Framework 4 开始,TPL 是编写多线程代码和并行代码的首选方法。但并不是所有代码都适合并行化,例如,如果某个循环在每次迭代时只执行少量工作,或它在很多次迭代时都不运行,那么并行化的开销可能导致代码运行更慢。 此外,像任何多线程代码一样,并行化会增加程序执行的复杂性。 尽管 TPL 简化了多线程方案,但建议对线程处理概念(例如,锁、死锁和争用条件)进行基本了解,以便能够有效地使用 TPL。
任务并行
“任务并行”是指一个或多个独立的任务同时运行。好处当然是系统资源的使用效率更高,可伸缩性更好;对于线程或工作项,可以使用更多的编程控件。因此,在 .NET Framework 中,TPL 是用于编写多线程、异步和并行代码的首选 API。
隐式创建和运行任务
Parallel.Invoke 方法提供了一种简便方式,可同时运行任意数量的任意语句。只需为每个工作项传入 Action 委托即可。
下面的示例演示 Invoke 调用,创建并启动同时运行三个任务。将对共享数据达尔文的《物种起源》执行三项操作:最长的词、最频繁出现的词和字数。这些操作都不修改源,因此它们可以直接并行执行。
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ParallelInvokeDemo
{
class Program
{
static void Main(string[] args)
{
// Retrieve Darwin's "Origin of the Species" from Gutenberg.org.
string[] words = CreateWordArray();
// Perform three tasks in parallel on the source array
Parallel.Invoke(() =>
{
Console.WriteLine("Begin first task...");
GetLongestWord(words);
}, // close first Action
() =>
{
Console.WriteLine("Begin second task...");
GetMostCommonWords(words);
}, //close second Action
() =>
{
Console.WriteLine("Begin third task...");
GetCountForWord(words, "species");
} //close third Action
); //close parallel.invoke
Console.WriteLine("Returned from Parallel.Invoke");
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
private static string GetLongestWord(string[] words)
{
var longestWord = (from w in words
orderby w.Length descending
select w).First();
Console.WriteLine("Task 1 -- The longest word is {0}", longestWord);
return longestWord;
}
private static void GetMostCommonWords(string[] words)
{
var frequencyOrder = from word in words
where word.Length > 6
group word by word into g
orderby g.Count() descending
select g.Key;
var commonWords = frequencyOrder.Take(10);
StringBuilder sb = new StringBuilder();
sb.AppendLine("Task 2 -- The most common words are:");
foreach (var v in commonWords)
{
sb.AppendLine(" " + v);
}
Console.WriteLine(sb.ToString());
}
private static void GetCountForWord(string[] words, string term)
{
var findWord = from word in words
where word.ToUpper().Contains(term.ToUpper())
select word;
Console.WriteLine(@"Task 3 -- The word ""{0}"" occurs {1} times.", term, findWord.Count());
}
static string[] CreateWordArray()
{
string s = System.IO.File.ReadAllText("Origin of the Species.txt");
// Separate string into an array of words, removing some common punctuation.
return s.Split(
new char[] { ' ', '\u000A', ',', '.', ';', ':', '-', '_', '/' },
StringSplitOptions.RemoveEmptyEntries);
}
}
}
//RESULT:
//Begin second task...
//Begin first task...
//Begin third task...
//Task 2 -- The most common words are:
// species
// selection
// varieties
// natural
// animals
// between
// different
// distinct
// several
// conditions
//Task 1 -- The longest word is characteristically
//Task 3 -- The word "species" occurs 1927 times.
//Returned from Parallel.Invoke
//Press any key to exit.