本文发表在 rolia.net 枫下论坛昨天受Deepblue的PLINQ启发,到微软网站上下载了一个Parallel Extensions to the .NET Framework 2008/6月的CPT版,简单试用了一下,感觉其功能强大,简单易用,如果你的计算机是2个核以上的,强烈推荐使用这一技术
步骤:
下载 Parallel Extensions to the .NET Framework
http://www.microsoft.com/downloads/details.aspx?FamilyId=348F73FD-593D-4B3C-B055-694C50D2B0F3&displaylang=en
然后安装,当然前提是你已经安装了.net 3.5
然后打开VS2008,在新建的Project中的References中加入System.Threading(这个时候System.Threading已经支持并行了,加入了一个Parallel class)
好了,现在就可以做一个小程序用并行技术试试行能提升有多强了
假设我们要copy 2个目录中的所有.txt文件,每个目录中有5000个txt files,我们先用传统的方式,一个一个的Copy,程序如下:
using System;
using System.Collections.Generic;
using System.IO
using System.Threading;
using System.Linq;
static void Lab()
{
DirectoryInfo[] sourceDirPool = new DirectoryInfo[]
{
new DirectoryInfo(@"c:\............."),
new DirectoryInfo(@"C:\.............")
};
string targetDirPath = @"D:\....................";
DirectoryInfo targetDir = new DirectoryInfo(targetDirPath);
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("一个一个的 Copy 开始..");
foreach (var s in sourceDirPool.SelectMany(x => x.GetFiles("*.txt")))
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString()); // 打印执行时间
}
我的机器 Core 2 6600, 2.4G, 执行时间 146秒
然后用并行技术重新改写 里面的Copy语句:
把
foreach (var s in sourceDirPool.SelectMany(x => x.GetFiles("*.txt")))
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
}
替换成
Parallel.ForEach(sourceDirPool.SelectMany(x => x.GetFiles("*.txt")),
s =>
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
});
这次时间变为可怕的81秒,是上一次顺序Copy的55%,性能提升接近一倍
这这里简单解释一下 Paraller.ForEach,这是一个static的函数,有很多不同的签名形式。
其中我在上边程序里面用到的签名是
ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);
第一个签名IEnumerable<TSource> source非常好理解,就是一个实现了IEnumerable接口的object,在这里是
sourceDirPool.SelectMany(x => x.GetFiles("*.txt")),这个SelectMany返回一个IEnumerable<FileInfo>的object,
里面存着两个目录中所有需要copy的.txt文件信息
第二个签名Action<TSource> body,刚开始看有些费解,其实很简单,Action<>系列是.net系统提供的函数delegate,它同样有很多变种,在这里
Action<TSource> body意思是 你必须实现这么一个函数,这个函数有一个输入参数TSource,但是没有返回值。所有的Action系列delegate都是没有返回值的。与之相对应的是另外一组经常在Linq里面用到的系统delegate,就是FUNC<>系列,它则必须有一个返回值,
比如我们经常用到的(IEnumerable.Where( 里面就是一个 FUNC<TSource,bool> body类型的degelate,意思是你的这个degelate必须返回一个bool的值)
在这里,我用了一个兰姆达表达式来表示这个Action, 就是 s=> {},用.net 2.0的语法可以用new delegate (string s) { ..... }来代替,1.1的语法就要在其他地方写一个函数,然后用函数名来代替。
有时候我在想C#在一步一步的和函数语言接轨,或许有一天F#和C#就看不出来有什么区别了
Paraller.ForEach只是Paraller类的其中一项激动人心的功能,类似的还有Paraller.Invoke,它可以并行执行用户所指定的任意多个函数,我还没有测试,但相信性能提升同样大大的更多精彩文章及讨论,请光临枫下论坛 rolia.net
步骤:
下载 Parallel Extensions to the .NET Framework
http://www.microsoft.com/downloads/details.aspx?FamilyId=348F73FD-593D-4B3C-B055-694C50D2B0F3&displaylang=en
然后安装,当然前提是你已经安装了.net 3.5
然后打开VS2008,在新建的Project中的References中加入System.Threading(这个时候System.Threading已经支持并行了,加入了一个Parallel class)
好了,现在就可以做一个小程序用并行技术试试行能提升有多强了
假设我们要copy 2个目录中的所有.txt文件,每个目录中有5000个txt files,我们先用传统的方式,一个一个的Copy,程序如下:
using System;
using System.Collections.Generic;
using System.IO
using System.Threading;
using System.Linq;
static void Lab()
{
DirectoryInfo[] sourceDirPool = new DirectoryInfo[]
{
new DirectoryInfo(@"c:\............."),
new DirectoryInfo(@"C:\.............")
};
string targetDirPath = @"D:\....................";
DirectoryInfo targetDir = new DirectoryInfo(targetDirPath);
var sw = new Stopwatch();
sw.Start();
Console.WriteLine("一个一个的 Copy 开始..");
foreach (var s in sourceDirPool.SelectMany(x => x.GetFiles("*.txt")))
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
}
sw.Stop();
Console.WriteLine(sw.Elapsed.ToString()); // 打印执行时间
}
我的机器 Core 2 6600, 2.4G, 执行时间 146秒
然后用并行技术重新改写 里面的Copy语句:
把
foreach (var s in sourceDirPool.SelectMany(x => x.GetFiles("*.txt")))
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
}
替换成
Parallel.ForEach(sourceDirPool.SelectMany(x => x.GetFiles("*.txt")),
s =>
{
s.CopyTo(Path.Combine(targetDir.FullName, s.Name));
});
这次时间变为可怕的81秒,是上一次顺序Copy的55%,性能提升接近一倍
这这里简单解释一下 Paraller.ForEach,这是一个static的函数,有很多不同的签名形式。
其中我在上边程序里面用到的签名是
ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);
第一个签名IEnumerable<TSource> source非常好理解,就是一个实现了IEnumerable接口的object,在这里是
sourceDirPool.SelectMany(x => x.GetFiles("*.txt")),这个SelectMany返回一个IEnumerable<FileInfo>的object,
里面存着两个目录中所有需要copy的.txt文件信息
第二个签名Action<TSource> body,刚开始看有些费解,其实很简单,Action<>系列是.net系统提供的函数delegate,它同样有很多变种,在这里
Action<TSource> body意思是 你必须实现这么一个函数,这个函数有一个输入参数TSource,但是没有返回值。所有的Action系列delegate都是没有返回值的。与之相对应的是另外一组经常在Linq里面用到的系统delegate,就是FUNC<>系列,它则必须有一个返回值,
比如我们经常用到的(IEnumerable.Where( 里面就是一个 FUNC<TSource,bool> body类型的degelate,意思是你的这个degelate必须返回一个bool的值)
在这里,我用了一个兰姆达表达式来表示这个Action, 就是 s=> {},用.net 2.0的语法可以用new delegate (string s) { ..... }来代替,1.1的语法就要在其他地方写一个函数,然后用函数名来代替。
有时候我在想C#在一步一步的和函数语言接轨,或许有一天F#和C#就看不出来有什么区别了
Paraller.ForEach只是Paraller类的其中一项激动人心的功能,类似的还有Paraller.Invoke,它可以并行执行用户所指定的任意多个函数,我还没有测试,但相信性能提升同样大大的更多精彩文章及讨论,请光临枫下论坛 rolia.net