5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

 

5天玩转C#并行和多线程编程系列文章目录

5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

5天玩转C#并行和多线程编程 —— 第四天 Task进阶

5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

在上一篇博客5天玩转C#并行和多线程编程 —— 第一天 认识Parallel中,我们学习了Parallel的用法。并行编程,本质上是多线程的编程,那么当多个线程同时处理一个任务的时候,必然会出现资源访问问题,及所谓的线程安全。就像现实中,我们开发项目,就是一个并行的例子,把不同的模块分给不同的人,同时进行,才能在短的时间内做出大的项目。如果大家都只管自己写自己的代码,写完后发现合并不到一起,那么这种并行就没有了意义。
  并行算法的出现,随之而产生的也就有了并行集合,及线程安全集合;微软向的也算周到,没有忘记linq,也推出了linq的并行版本,plinq - Parallel Linq.
 
 一、并行集合 —— 线程安全集合
  并行计算使用的多个线程同时进行计算,所以要控制每个线程对资源的访问,我们先来看一下平时常用的List<T>集合,在并行计算下的表现,新建一个控制台应用程序,添加一个PEnumerable类(当然你也直接写到main方法里面测试,建议分开写),写如下方法:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Concurrent; namespace ThreadPool
{
public class PEnumerable
{
public static void ListWithParallel()
{
List<int> list = new List<int>();
Parallel.For(0, 10000, item =>
{
list.Add(item);
});
Console.WriteLine("List's count is {0}",list.Count());
}
}
}

点击F5运行,得到如下结果:

看到结果中显示的5851,但是我们循环的是10000次啊!怎么结果不对呢?这是因为List<T>是非线程安全集合,意思就是说所有的线程都可以修改他的值。

下面我们来看下并行集合 —— 线程安全集合,在System.Collections.Concurrent命名空间中,首先来看一下ConcurrentBag<T>泛型集合,其用法和List<T>类似,先来写个方法测试一下:

public static void ConcurrentBagWithPallel()
{
ConcurrentBag<int> list = new ConcurrentBag<int>();
Parallel.For(0, 10000, item =>
{
list.Add(item);
});
Console.WriteLine("ConcurrentBag's count is {0}", list.Count());
}

同时执行两个方法,结果如下:

可以看到,ConcurrentBag集合的结果是正确的。下面我们修改代码看看ConcurrentBag里面的数据到底是怎么存放的,修改代码如下:

public static void ConcurrentBagWithPallel()
{
ConcurrentBag<int> list = new ConcurrentBag<int>();
Parallel.For(0, 10000, item =>
{
list.Add(item);
});
Console.WriteLine("ConcurrentBag's count is {0}", list.Count());
int n = 0;
foreach(int i in list)
{
if (n > 10)
break;
n++;
Console.WriteLine("Item[{0}] = {1}",n,i);
}
Console.WriteLine("ConcurrentBag's max item is {0}", list.Max()); }

先来看一下运行结果:

可以看到,ConcurrentBag中的数据并不是按照顺序排列的,顺序是乱的,随机的。我们平时使用的Max、First、Last等linq方法都还有。其时分类似Enumerable的用法,大家可以参考微软的MSDN了解它的具体用法。

关于线程安全的集合还有很多,和我们平时用的集合都差不多,比如类似Dictionary的ConcurrentDictionary,还有ConcurrentStack,ConcurrentQueue等。

 二、Parallel Linq的用法及性能

1、AsParallel

前面了解了并行的For和foreach,今天就来看一下Linq的并行版本是怎么样吧?为了测试,我们添加一个Custom类,代码如下:

public class Custom
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
}

写如下测试代码:

 public static void TestPLinq()
{
Stopwatch sw = new Stopwatch();
List<Custom> customs = new List<Custom>();
for (int i = 0; i < 2000000; i++)
{
customs.Add(new Custom() { Name = "Jack", Age = 21, Address = "NewYork" });
customs.Add(new Custom() { Name = "Jime", Age = 26, Address = "China" });
customs.Add(new Custom() { Name = "Tina", Age = 29, Address = "ShangHai" });
customs.Add(new Custom() { Name = "Luo", Age = 30, Address = "Beijing" });
customs.Add(new Custom() { Name = "Wang", Age = 60, Address = "Guangdong" });
customs.Add(new Custom() { Name = "Feng", Age = 25, Address = "YunNan" });
} sw.Start();
var result = customs.Where<Custom>(c => c.Age > 26).ToList();
sw.Stop();
Console.WriteLine("Linq time is {0}.",sw.ElapsedMilliseconds); sw.Restart();
sw.Start();
var result2 = customs.AsParallel().Where<Custom>(c => c.Age > 26).ToList();
sw.Stop();
Console.WriteLine("Parallel Linq time is {0}.", sw.ElapsedMilliseconds);
}

其实也就是加了一个AsParallel()方法,下面来看下运行结果:

时间相差了一倍,不过有时候不会相差这么多,要看系统当前的资源利用率。大家可以多测试一下。

其实,AsParallel()这个方法可以应用与任何集合,包括List<T>集合,从而提高查询速度和系统性能。

2、GroupBy方法

在项目中,我们经常要对数据做处理,比如分组统计,我们知道在linq中也可以实现,今天来学习一下新的ToLookup方法,写一个测试方法,代码如下:

public static void OrderByTest()
{
Stopwatch stopWatch = new Stopwatch();
List<Custom> customs = new List<Custom>();
for (int i = 0; i < 2000000; i++)
{
customs.Add(new Custom() { Name = "Jack", Age = 21, Address = "NewYork" });
customs.Add(new Custom() { Name = "Jime", Age = 26, Address = "China" });
customs.Add(new Custom() { Name = "Tina", Age = 29, Address = "ShangHai" });
customs.Add(new Custom() { Name = "Luo", Age = 30, Address = "Beijing" });
customs.Add(new Custom() { Name = "Wang", Age = 60, Address = "Guangdong" });
customs.Add(new Custom() { Name = "Feng", Age = 25, Address = "YunNan" });
} stopWatch.Restart();
var groupByAge = customs.GroupBy(item => item.Age).ToList();
foreach (var item in groupByAge)
{
Console.WriteLine("Age={0},count = {1}", item.Key, item.Count());
}
stopWatch.Stop(); Console.WriteLine("Linq group by time is: " + stopWatch.ElapsedMilliseconds); stopWatch.Restart();
var lookupList = customs.ToLookup(i => i.Age);
foreach (var item in lookupList)
{
Console.WriteLine("LookUP:Age={0},count = {1}", item.Key, item.Count());
}
stopWatch.Stop();
Console.WriteLine("LookUp group by time is: " + stopWatch.ElapsedMilliseconds);
}

运行结果如下:

ToLookup方法是将集合转换成一个只读集合,所以在大数据量分组时性能优于List.大家可以查阅相关资料,这里由于篇幅问题,不再细说。

 
分类: C#

C#并行和多线程编程的更多相关文章

  1. 5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  2. 5天玩转C#并行和多线程编程 —— 第四天 Task进阶

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  3. 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  4. 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  5. 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  6. 5天玩转C#并行和多线程编程

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  7. 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 转载 https://www.cnblogs.com/yunfeifei/p/3993401.html

    5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编 ...

  8. C#并行和多线程编程_(1)认识Parallel

    Parallel: 英 [ˈpærəlel]    美 [ˈpærəˌlɛl] ,并联的,并行的. 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性 ...

  9. python 常见错误和异常 函数 正则表达式及多线程编程

    生成随机密码#!/usr/bin/env python import stringfrom random import choice def gen_pass(num=9): all_chs = st ...

随机推荐

  1. Shell在大数据的魅力时代:从一点点思路百度大数据面试题

    供Linux开发中的同学们,Shell这可以说是一个基本功. 对于同学们的操作和维护.Shell也可以说是一种必要的技能,Shell.对于Release Team,软件配置管理的同学来说.Shell也 ...

  2. Apple Watch 1.0 开发介绍 2.1 WatchKit Apps UI要点

    实现app的开始是定义storyboard场景.每个场景定义了app的一部分界面.可以为不同的尺寸自定义场景. 组装storyboard界面 WatchKit app和iOS app的布局模式不同.组 ...

  3. ios 多线程开发(二)线程管理

    线程管理 iOS和OS X中每一个进程(或程序)由一个或多个线程组成.程序由一个运行main方法的线程开始,中间可以产生其他线程来执行一些指定的功能. 当程序产生一个新线程后,这个线程在程序进程空间内 ...

  4. c++ stl algorithm: std::find, std::find_if

    std::find: 查找容器元素, find仅仅能查找容器元素为<基本数据类型> [cpp] view plaincopy #include <iostream> #incl ...

  5. IBatis增删改差的实现以及注意点

    此次进讲述对表操作的实现细节.废话不多说,代码见真章. <?xml version="1.0" encoding="utf-8" ?> <sq ...

  6. 安装framework 3.5出现0x800F0922的解决方法

    关闭Windows 防火墙,启动Windows model install服务 重启! 再安装 就ok了

  7. 【Android进阶】Application对象的详解

    1:Application是什么? Application和Activity,Service一样,是android框架的一个系统组件,当android程序启动时系统会创建一个 application对 ...

  8. Hibernate一个简短的引论

    我们从几个方面进行阐述Hibernate When? What ? How? When? Hibernate由来是因为当时EJBBean1.1在处理entittBean架构时,花费的时间要比业务逻辑很 ...

  9. 使用 gridfs-stream 存储文件遇到的一个坑。

    前一段时间参读了某个coder写的用 gridfs-stream 来存储文件,感觉不错就自己用 gridfs-stream 模块写了一个文件存储服务,但是发现存储的文件总是删不掉, 我调用的是GFS的 ...

  10. COCOFrame

    COCOFrame它是一个轻量级的开发框架,综合Android APP开发过程中经常使用的工具,方法,特效.当中F类採用的AndroidQuery实现,做了进一步的封装及部分优化. 1. Jar包大小 ...