代码改变世界

C#并行化Parallel简单使用示例

2014-04-14 19:52  hduhans  阅读(559)  评论(0编辑  收藏  举报

  .NET Framework 4.0中的TPL(Task Parallel Library)支持了并行化计算,可合理地运用在实际项目开发过程中,以提高程序的执行效率。

1、Parallel.For循环

     本例分别对普通遍历求和计算NoParallel,无同步Lock的并行化求和计算ParallelNoLock和并行化求和有同步的Lock并行化求和计算ParallelLock执行了5次计算,这三个方法都做相同的事情,就是计算1-20的和并在每次循环后线程休眠50毫秒(模拟真实大运算环境),程序代码及运行结果如下:

class A
{
    //普通遍历求和计算
    public void NoParallel(int times) 
    {
        Stopwatch sw = Stopwatch.StartNew();
        int result = 0;
        for (int i = 0; i < times; i++) {
            result += i;
            Thread.Sleep(50);  //为模拟大量计算,线程休眠50毫秒,下同
        }
        Console.WriteLine("普通遍历,结果:{0},耗时:{1}毫秒",result,sw.ElapsedMilliseconds);
    }

    //使用了并行化求和计算,没有同步Lock
    public void ParallelNoLock(int times)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int result = 0;
        Parallel.For(0, times, i => {
            result += i;
            Thread.Sleep(50);
        });
        Console.WriteLine("并行遍历(无Lock),结果:{0},耗时:{1}毫秒", result, sw.ElapsedMilliseconds);
    }

    private Object syncHandle = new object();

    //使用了并行化求和计算,使用了同步Lock
    public void ParallelLock(int times)
    {
        Stopwatch sw = Stopwatch.StartNew();
        int result = 0;
        Parallel.For(0, times, i => {
            lock (syncHandle)
            {
                result += i;
            }
            Thread.Sleep(50);
        });
        Console.WriteLine("并行遍历(有Lock),结果:{0},耗时:{1}毫秒", result, sw.ElapsedMilliseconds);
    }

}

//调用
A a = new A();

Console.WriteLine("普通遍历=>");
for (int i = 0; i < 5; i++)
{
    a.NoParallel(20);
}

Console.WriteLine("并行遍历(无Lock)=>");
for (int i = 0; i < 5; i++)
{
    a.ParallelNoLock(20);
}

Console.WriteLine("并行遍历(有Lock)=>");
for (int i = 0; i < 5; i++)
{
    a.ParallelLock(20);
}
View Code

  执行结果如下:

 

从结果可知,使用Parallel.For并行化遍历时,效率比普通for循环效率高好多倍,但是由于Parallel使用了多线程,为保证程序的可再现性,必须使用同步Lock。

 

2、Parallel.ForEach循环

   本例将Customer对象序列化成XMl并保存到字典Dictionary中,比较了传统的实现方式和并行化的实现方式效率的差别。代码即运行结果如下:

public class ParallelForEach
{

    //自定义Customer类型
    public class Customer
    {
        public long ID { get; set; }

        public String Name { get; set; }

        public override string ToString()
        {
            return Name;
        }

    }

    //普通处理方式
    public static Dictionary<long,String> SerializeCustomers(Customer[] customers)
    {
        var dict = new Dictionary<long, string>();
        var xmlSerializer = new XmlSerializer(typeof(Customer));
        foreach (var customer in customers)
        {
            using (var ms = new MemoryStream())
            {
                xmlSerializer.Serialize(ms, customer);
                dict.Add(customer.ID,Encoding.ASCII.GetString(ms.ToArray()));
            }
            Thread.Sleep(50);
        }
        return dict;
    }

    //并行化处理方式
    public static Dictionary<long, String> ParallelSerializeCustomers(Customer[] customers)
    {
        var dict = new Dictionary<long, string>();
        var xmlSerializer = new XmlSerializer(typeof(Customer));
        object lockObj = new object();
        Parallel.ForEach(customers, () => new Dictionary<long, String>(),
            (customer, loopState,lo,single) => {
                using (var ms = new MemoryStream()) {
                    xmlSerializer.Serialize(ms, customer);
                    single.Add(customer.ID, Encoding.ASCII.GetString(ms.ToArray()));
                }
                return single;
            },
            (single) => {
                lock (lockObj)
                {
                    single.ToList().ForEach(p=>dict.Add(p.Key,p.Value));
                }
                Thread.Sleep(50);
            }
        );
        return dict;
    }

}

//执行测试
ParallelForEach.Customer[] customers = new ParallelForEach.Customer[50];
for (int i = 0; i < customers.Length; i++) {
    customers[i] = new ParallelForEach.Customer() { ID = i, Name = "name" + i.ToString() };
}

//执行耗时2657ms
Dictionary<long, String> dic1 = ParallelForEach.SerializeCustomers(customers);

//执行耗时65ms
Dictionary<long, String> dic2 = ParallelForEach.ParallelSerializeCustomers(customers);
View Code

  执行结果分别为2657ms和65ms,可知并行化计算效率比普通遍历效率高。

 

3、适用环境

    并非任何环境都适合多线程并行化计算,也即并非多线程一定能提高效率,其适用条件如下:

  1) 运行环境是多核操作系统;

    2) 执行任务并非纯运算,因为纯运算任务使用多线程反而会降低效率;

    3) 任务中有缓冲操作,如I/O操作,读数据库等操作;