C#并行和多线程编程

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#

时间: 2024-10-29 19:06:01

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

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

在上一篇博客5天玩转C#并行和多线程编程 —— 第一天 认识Parallel中,我们学习了Parallel的用法.并行编程,本质上是多线程的编程,那么当多个线程同时处理一个任务的时候,必然会出现资源访问问题,及所谓的线程安全.就像现实中,我们开发项目,就是一个并行的例子,把不同的模块分给不同的人,同时进行,才能在短的时间内做出大的项目.如果大家都只管自己写自己的代码,写完后发现合并不到一起,那么这种并行就没有了意义. 并行算法的出现,随之而产生的也就有了并行集合,及线程安全集合:微软向的也算周到

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

5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task 5天玩转C#并行和多线程编程 —— 第四天 Task进阶 5天玩转C#并行和多线程编程 —— 第五天 多线程编程大总结 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了

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

5天玩转C#并行和多线程编程系列文章目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task 5天玩转C#并行和多线程编程 —— 第四天 Task进阶  一.多线程带来的问题 1.死锁问题  前面我们学习了Task的使用方法,其中Task的等待机制让我们瞬间爱上了它,但是如果我们在调用Task.WaitAll方法等待所有线程时,如果有一个Task一直不返

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

5天玩转C#并行和多线程编程系列文章目录,前三天的目录,最后一篇更新完会附上完整目录 5天玩转C#并行和多线程编程 —— 第一天 认识Parallel 5天玩转C#并行和多线程编程 —— 第二天 并行集合和PLinq 5天玩转C#并行和多线程编程 —— 第三天 认识和使用Task  一.Task的嵌套 Task中还可以再嵌套Task,Thread中能不能这样做,我只能说我是没这样写过.Task中的嵌套,我感觉其实也可以分开来写,不过嵌套起来会方便管理一点.Task中的嵌套分为两种,关联嵌套和非关

C#并行和多线程编程 3——认识和使用Task

对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于"任务的编程模型"所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?  任务和线程的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比

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

对于多线程,我们经常使用的是Thread.在我们了解Task之前,如果我们要使用多核的功能可能就会自己来开线程,然而这种线程模型在.net 4.0之后被一种称为基于“任务的编程模型”所冲击,因为task会比thread具有更小的性能开销,不过大家肯定会有疑惑,任务和线程到底有什么区别呢?  任务和线程的区别: 1.任务是架构在线程之上的,也就是说任务最终还是要抛给线程去执行. 2.任务跟线程不是一对一的关系,比如开10个任务并不是说会开10个线程,这一点任务有点类似线程池,但是任务相比线程池有很

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

随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threading.Tasks.这里面有很多关于并行开发的东西,今天第一篇就介绍下最基础,最简单的——认识和使用Parallel. 一. Parallel的使用 在Parallel下面有三个常用的方法invoke,For和ForEach. 1.  Parallel.Invoke 这是最简单,最简洁的将串行的代码并行化. 在这

18 多线程编程 - 《Python 核心编程》

?? 引言/动机 ?? 线程和进程 ?? 线程和 Python ?? thread 模块 ?? threading 模块 ?? 生产者-消费者问题和 Queue 模块 ?? 相关模块 18.1 引言/动机 18.2 线程和进程 什么是进程? 计算机程序只不过是磁盘中可执行的,二进制(或其它类型)的数据.它们只有在被读取到内 存中,被操作系统调用的时候才开始它们的生命期.进程(有时被称为重量级进程)是程序的一次 执行.每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据.操作系

多线程编程、Java I/O系统和Java图形界面编程

多线程编程: 一个正在运行的程序通常称为一个进程,每一个任务称为一个线程,中能够在一个程序内运行多线程的程序称为多线程程序. 线程与进程的区别:①每个进程都需要操作系统为其分配独立的内存空间: ②而同一个进程中的所有线程都在同一内存空间工作,这些线程可以共享同一块内存和系统资源. 线程的创建: 1)通过继承Thread类来创建线程: ①创建一个继承Thread类的类: ②在创建的Thread子类中重写run()方法,在方法中写入想要线程运行的代码: ③创建Thread子类的实例: ④通过调用该实