服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - c# 并行和多线程编程——认识Parallel

c# 并行和多线程编程——认识Parallel

2022-11-02 10:56雲霏霏 C#

这篇文章主要介绍了c# 并行和多线程编程的相关资料,帮助大家更好的理解和学习c# Parallel的相关知识,感兴趣的朋友可以了解下

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

一、 Parallel的使用

在Parallel下面有三个常用的方法invoke,For和ForEach。

1、Parallel.Invoke

这是最简单,最简洁的将串行的代码并行化。

在这里先讲一个知识点,就是StopWatch的使用,最近有一些人说找不到StopWatch,StopWatch到底是什么东西,今天就来说明一下。

StopWatch在System.Diagnostics命名控件,要使用它就要先引用这个命名空间。

其使用方法如下:

?
1
2
3
4
5
6
7
8
9
10
11
var stopWatch = new StopWatch();   //创建一个Stopwatch实例
 
stopWatch.Start();   //开始计时
 
stopWatch.Stop();   //停止计时
 
stopWatch.Reset();  //重置StopWatch
 
stopWatch.Restart(); //重新启动被停止的StopWatch
 
stopWatch.ElapsedMilliseconds //获取stopWatch从开始到现在的时间差,单位是毫秒

本次用到的就这么多知识点,想了解更多关于StopWatch的,去百度一下吧,网上有很多资料。

下面进入整体,开始介绍Parallel.Invoke方法,废话不多说了,首先新建一个控制台程序,添加一个类,代码如下:

?
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
public class ParallelDemo
 {
 private Stopwatch stopWatch = new Stopwatch();
 
 public void Run1()
 {
 Thread.Sleep(2000);
 Console.WriteLine("Task 1 is cost 2 sec");
 }
 public void Run2()
 {
 Thread.Sleep(3000);
 Console.WriteLine("Task 2 is cost 3 sec");
 }
 
 public void ParallelInvokeMethod()
 {
 stopWatch.Start();
 Parallel.Invoke(Run1, Run2);
 stopWatch.Stop();
 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 stopWatch.Restart();
 Run1();
 Run2();
 stopWatch.Stop();
 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms.");
 }

代码很简单,首先新加一个类,在类中写了两个方法,Run1和Run2,分别等待一定时间,输出一条信息,然后写了一个测试方法ParallelInvokeMethod,分别用两种方法调用Run1和Run2,然后在main方法中调用,下面来看一下运行时间如何:

c# 并行和多线程编程——认识Parallel

  大家应该能够猜到,正常调用的话应该是5秒多,而Parallel.Invoke方法调用用了只有3秒,也就是耗时最长的那个方法,可以看出方法是并行执行的,执行效率提高了很多。

2、Parallel.For

这个方法和For循环的功能相似,下面就在类中添加一个方法来测试一下吧。代码如下:

?
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
public void ParallelForMethod()
 {
 stopWatch.Start();
 for (int i = 0; i < 10000; i++)
 {
 for (int j = 0; j < 60000; j++)
 {
  int sum = 0;
  sum += i;
 }
 }
 stopWatch.Stop();
 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 stopWatch.Reset();
 stopWatch.Start();
 Parallel.For(0, 10000, item =>
 {
 for (int j = 0; j < 60000; j++)
 {
  int sum = 0;
  sum += item;
 }
 });
 stopWatch.Stop();
 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 }

写了两个循环,做了一些没有意义的事情,目的主要是为了消耗CPU时间,同理在main方法中调用,运行结果如下图:

c# 并行和多线程编程——认识Parallel

可以看到,Parallel.For所用的时间比单纯的for快了1秒多,可见提升的性能是非常可观的。那么,是不是Parallel.For在任何时候都比for要快呢?答案当然是“不是”,要不然微软还留着for干嘛?

下面修改一下代码,添加一个全局变量num,代码如下:

?
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
36
37
public void ParallelForMethod()
 {
 var obj = new Object();
 long num = 0;
 ConcurrentBag<long> bag = new ConcurrentBag<long>();
 
 stopWatch.Start();
 for (int i = 0; i < 10000; i++)
 {
 for (int j = 0; j < 60000; j++)
 {
  //int sum = 0;
  //sum += item;
  num++;
 }
 }
 stopWatch.Stop();
 Console.WriteLine("NormalFor run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 stopWatch.Reset();
 stopWatch.Start();
 Parallel.For(0, 10000, item =>
 {
 for (int j = 0; j < 60000; j++)
 {
  //int sum = 0;
  //sum += item;
  lock (obj)
  {
  num++;
  }
 }
 });
 stopWatch.Stop();
 Console.WriteLine("ParallelFor run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 }

Parallel.For由于是并行运行的,所以会同时访问全局变量num,为了得到正确的结果,要使用lock,此时来看看运行结果:

c# 并行和多线程编程——认识Parallel

  是不是大吃一惊啊?Parallel.For竟然用了15秒多,而for跟之前的差不多。这主要是由于并行同时访问全局变量,会出现资源争夺,大多数时间消耗在了资源等待上面。

一直说并行,那么从哪里可以看出来Parallel.For是并行执行的呢?下面来写个测试代码:

?
1
2
3
4
Parallel.For(0, 100, i =>
 {
 Console.Write(i + "\t");
 });

从0输出到99,运行后会发现输出的顺序不对,用for顺序肯定是对的,并行同时执行,所以会出现输出顺序不同的情况。

3、Parallel.Foreach

这个方法跟Foreach方法很相似,想具体了解的,可以百度些资料看看,这里就不多说了,下面给出其使用方法:

?
1
2
3
4
5
6
List<int> list = new List<int>();
 list.Add(0);
 Parallel.ForEach(list, item =>
 {
 DoWork(item);
 });

二、 Parallel中途退出循环和异常处理

1、当我们使用到Parallel,必然是处理一些比较耗时的操作,当然也很耗CPU和内存,如果我们中途向停止,怎么办呢?

在串行代码中我们break一下就搞定了,但是并行就不是这么简单了,不过没关系,在并行循环的委托参数中提供了一个ParallelLoopState,

该实例提供了Break和Stop方法来帮我们实现。

Break: 当然这个是通知并行计算尽快的退出循环,比如并行计算正在迭代100,那么break后程序还会迭代所有小于100的。

Stop:这个就不一样了,比如正在迭代100突然遇到stop,那它啥也不管了,直接退出。

下面来写一段代码测试一下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void ParallelBreak()
 {
 ConcurrentBag<int> bag = new ConcurrentBag<int>();
 stopWatch.Start();
 Parallel.For(0, 1000, (i, state) =>
 {
 if (bag.Count == 300)
 {
  state.Stop();
  return;
 }
 bag.Add(i);
 });
 stopWatch.Stop();
 Console.WriteLine("Bag count is " + bag.Count + ", " + stopWatch.ElapsedMilliseconds);
 }

这里使用的是Stop,当数量达到300个时,会立刻停止;可以看到结果"Bag count is 300",如果用break,可能结果是300多个或者300个,大家可以测试一下。

2、异常处理

  首先任务是并行计算的,处理过程中可能会产生n多的异常,那么如何来获取到这些异常呢?普通的Exception并不能获取到异常,然而为并行诞生的AggregateExcepation就可以获取到一组异常。

这里我们修改Parallel.Invoke的代码,修改后代码如下:

?
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class ParallelDemo
 {
 private Stopwatch stopWatch = new Stopwatch();
 
 public void Run1()
 {
 Thread.Sleep(2000);
 Console.WriteLine("Task 1 is cost 2 sec");
 throw new Exception("Exception in task 1");
 }
 public void Run2()
 {
 Thread.Sleep(3000);
 Console.WriteLine("Task 2 is cost 3 sec");
 throw new Exception("Exception in task 2");
 }
 
 public void ParallelInvokeMethod()
 {
 stopWatch.Start();
 try
 {
 Parallel.Invoke(Run1, Run2);
 }
 catch (AggregateException aex)
 {
 foreach (var ex in aex.InnerExceptions)
 {
  Console.WriteLine(ex.Message);
 }
 }
 stopWatch.Stop();
 Console.WriteLine("Parallel run " + stopWatch.ElapsedMilliseconds + " ms.");
 
 stopWatch.Reset();
 stopWatch.Start();
 try
 {
 Run1();
 Run2();
 }
 catch(Exception ex)
 {
 Console.WriteLine(ex.Message);
 }
 stopWatch.Stop();
 Console.WriteLine("Normal run " + stopWatch.ElapsedMilliseconds + " ms.");
 }

顺序调用方法我把异常处理写一起了,这样只能捕获Run1的异常信息,大家可以分开写。捕获AggregateException 异常后,用foreach循环遍历输出异常信息,可以看到两个异常信息都显示了。

点击这里,下载源码

以上就是c# 并行和多线程编程——认识Parallel的详细内容,更多关于c# 并行和多线程编程的资料请关注服务器之家其它相关文章!

原文链接:https://www.cnblogs.com/yunfeifei/p/3993401.html

延伸 · 阅读

精彩推荐
  • C#C#访问SQLServer增删改查代码实例

    C#访问SQLServer增删改查代码实例

    这篇文章主要为大家详细介绍了C#访问SQLServer增删改查代码实例,感兴趣的小伙伴们可以参考一下...

    bcbobo21cn12102021-12-06
  • C#c#中string的特性介绍及注意事项小结

    c#中string的特性介绍及注意事项小结

    这篇文章主要给大家介绍了关于c#中string的特性介绍及注意事项的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用c#具有一定的参考学习...

    猴子哥4072022-08-09
  • C#C#写差异文件备份工具的示例

    C#写差异文件备份工具的示例

    这篇文章主要介绍了C#写差异文件备份工具的示例,帮助大家利用c#备份,管理文件,感兴趣的朋友可以了解下...

    黑衫老腰3552022-10-12
  • C#在winform下实现左右布局多窗口界面的方法

    在winform下实现左右布局多窗口界面的方法

    在web页面上我们可以通过frameset,iframe嵌套框架很容易实现各种导航+内容的布局界面,而在winform、WPF中实现其实也很容易,通过本文给大家介绍在winform下实...

    梦在旅途5602021-11-14
  • C#C#实现俄罗斯方块基本功能

    C#实现俄罗斯方块基本功能

    这篇文章主要为大家详细介绍了C#实现俄罗斯方块的基本功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    7176066414852022-08-29
  • C#C#用委托BeginInvoke做异步线程

    C#用委托BeginInvoke做异步线程

    今天小编就为大家分享一篇关于C#用委托BeginInvoke做异步线程,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来...

    chenqiangdage11472022-03-08
  • C#C#编程实现获取文件夹中所有文件的文件名

    C#编程实现获取文件夹中所有文件的文件名

    这篇文章主要介绍了C#编程实现获取文件夹中所有文件的文件名,可实现获取特定目录下制定类型文件名称的功能,涉及C#针对文件与目录的遍历、查询等操作...

    Jan.David7532021-11-02
  • C#C#方法中参数ref和out详解

    C#方法中参数ref和out详解

    这篇文章主要为大家详细介绍了C#方法中参数ref和out的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    Crazy Struggle3762022-01-25