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方法里面测试,建议分开写),写如下方法:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Threading.Tasks;
6using System.Collections.Concurrent;
7
8namespace ThreadPool
9{
10   public class PEnumerable
11   {
12      public static void ListWithParallel()
13      {
14         List&lt;int&gt; list = new List&lt;int&gt;();
15         Parallel.For(0, 10000, item =&gt;
16         {
17            list.Add(item);
18         });
19         Console.WriteLine(&quot;List&#x27;s count is {0}&quot;,list.Count());
20      }
21   }
22}
23

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

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

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

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


1
2
3
4
5
6
7
8
9
10
1public static void ConcurrentBagWithPallel()
2      {
3         ConcurrentBag&lt;int&gt; list = new ConcurrentBag&lt;int&gt;();
4         Parallel.For(0, 10000, item =&gt;
5         {
6            list.Add(item);
7         });
8         Console.WriteLine(&quot;ConcurrentBag&#x27;s count is {0}&quot;, list.Count());
9      }
10

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

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

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1public static void ConcurrentBagWithPallel()
2      {
3         ConcurrentBag&lt;int&gt; list = new ConcurrentBag&lt;int&gt;();
4         Parallel.For(0, 10000, item =&gt;
5         {
6            list.Add(item);
7         });
8         Console.WriteLine(&quot;ConcurrentBag&#x27;s count is {0}&quot;, list.Count());
9         int n = 0;
10         foreach(int i in list)
11         {
12            if (n &gt; 10)
13               break;
14            n++;
15            Console.WriteLine(&quot;Item[{0}] = {1}&quot;,n,i);
16         }
17         Console.WriteLine(&quot;ConcurrentBag&#x27;s max item is {0}&quot;, list.Max());
18
19      }
20

先来看一下运行结果:

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

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

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

 

** 二、Parallel Linq的用法及性能**

1、AsParallel

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


1
2
3
4
5
6
7
1public class Custom
2   {
3      public string Name { get; set; }
4      public int Age { get; set; }
5      public string Address { get; set; }
6   }
7

 写如下测试代码:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1 public static void TestPLinq()
2      {
3         Stopwatch sw = new Stopwatch();
4         List&lt;Custom&gt; customs = new List&lt;Custom&gt;();
5         for (int i = 0; i &lt; 2000000; i++)
6         {
7            customs.Add(new Custom() { Name = &quot;Jack&quot;, Age = 21, Address = &quot;NewYork&quot; });
8            customs.Add(new Custom() { Name = &quot;Jime&quot;, Age = 26, Address = &quot;China&quot; });
9            customs.Add(new Custom() { Name = &quot;Tina&quot;, Age = 29, Address = &quot;ShangHai&quot; });
10            customs.Add(new Custom() { Name = &quot;Luo&quot;, Age = 30, Address = &quot;Beijing&quot; });
11            customs.Add(new Custom() { Name = &quot;Wang&quot;, Age = 60, Address = &quot;Guangdong&quot; });
12            customs.Add(new Custom() { Name = &quot;Feng&quot;, Age = 25, Address = &quot;YunNan&quot; });
13         }
14
15         sw.Start();
16         var result = customs.Where&lt;Custom&gt;(c =&gt; c.Age &gt; 26).ToList();
17         sw.Stop();
18         Console.WriteLine(&quot;Linq time is {0}.&quot;,sw.ElapsedMilliseconds);
19
20         sw.Restart();
21         sw.Start();
22         var result2 = customs.AsParallel().Where&lt;Custom&gt;(c =&gt; c.Age &gt; 26).ToList();
23         sw.Stop();
24         Console.WriteLine(&quot;Parallel Linq time is {0}.&quot;, sw.ElapsedMilliseconds);
25      }
26

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

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

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

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

 

2、GroupBy方法

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
1public static void OrderByTest()
2      {
3         Stopwatch stopWatch = new Stopwatch();
4         List&lt;Custom&gt; customs = new List&lt;Custom&gt;();
5         for (int i = 0; i &lt; 2000000; i++)
6         {
7            customs.Add(new Custom() { Name = &quot;Jack&quot;, Age = 21, Address = &quot;NewYork&quot; });
8            customs.Add(new Custom() { Name = &quot;Jime&quot;, Age = 26, Address = &quot;China&quot; });
9            customs.Add(new Custom() { Name = &quot;Tina&quot;, Age = 29, Address = &quot;ShangHai&quot; });
10            customs.Add(new Custom() { Name = &quot;Luo&quot;, Age = 30, Address = &quot;Beijing&quot; });
11            customs.Add(new Custom() { Name = &quot;Wang&quot;, Age = 60, Address = &quot;Guangdong&quot; });
12            customs.Add(new Custom() { Name = &quot;Feng&quot;, Age = 25, Address = &quot;YunNan&quot; });
13         }
14
15         stopWatch.Restart();
16         var groupByAge = customs.GroupBy(item =&gt; item.Age).ToList();
17         foreach (var item in groupByAge)
18         {
19            Console.WriteLine(&quot;Age={0},count = {1}&quot;, item.Key, item.Count());
20         }
21         stopWatch.Stop();
22
23         Console.WriteLine(&quot;Linq group by time is: &quot; + stopWatch.ElapsedMilliseconds);
24
25
26         stopWatch.Restart();
27         var lookupList = customs.ToLookup(i =&gt; i.Age);
28         foreach (var item in lookupList)
29         {
30            Console.WriteLine(&quot;LookUP:Age={0},count = {1}&quot;, item.Key, item.Count());
31         }
32         stopWatch.Stop();
33         Console.WriteLine(&quot;LookUp group by time is: &quot; + stopWatch.ElapsedMilliseconds);
34      }
35

运行结果如下:

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

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

 

 

 

给TA打赏
共{{data.count}}人
人已打赏
安全技术

Bootstrap 间隔 (Spacing)

2021-12-21 16:36:11

安全技术

从零搭建自己的SpringBoot后台框架(二十三)

2022-1-12 12:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索