前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C#并行与多线程——Parallel并行

C#并行与多线程——Parallel并行

作者头像
李郑
发布2019-12-09 11:08:29
4.2K0
发布2019-12-09 11:08:29
举报

从线程说起

线程分为软件线程和硬件线程。

硬件线程又叫做逻辑内核,我们可以在”任务管理器“中查看”性能“标签页,就能查看电脑的线程数,我们常说的四核八线程,就是指这个,当然这里的四核八线程,其中的八线程是超线程技术,也就是一个核心对应两个线程,从而从硬件层面提升执行性能。

至于软件线程,我们知道一般来说代码都是串行的,就一个主线程,当我们为了实现加速而开了很多工作线程,这些工作线程,也就是软件线程。

线程管理

在.net 4.0之后的版本中,微软给我们提供了一个新的命名空间:System.Threading.Tasks。

这个命名空间提供了一系列的操作类来供我们对线程进行控制。

并行Parallel

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

先说下StopWatch,这个类主要用于测速,记录时间。

StopWatch 位于 System.Diagnostics命名空间,StopWatch有如下方法:

  • var stopWatch = new StopWatch(); //创建一个Stopwatch实例
  • stopWatch.Start(); //开始计时
  • stopWatch.Stop(); //停止计时
  • stopWatch.Reset(); //重置StopWatch
  • stopWatch.Restart(); //重新启动被停止的StopWatch
  • stopWatch.ElapsedMilliseconds //获取stopWatch从开始到现在的时间差,单位是毫秒

引入System.Threading(MSDN-System.Threading Namespace) 和 System.Threading.Tasks(MSDN-System.Threading.Tasks Namespace) 来使用Parallel。

Parallel.invoke()

public void Run1()
{
    Thread.Sleep(2000);
    Console.WriteLine("Task 1 is cost 2 sec");
}

public void Run2()
{
    Thread.Sleep(3000);
    Console.WriteLine("Task 1 is cost 3 sec");
}

写两个方法,一个让线程睡眠2s(2000ms),另一个让线程睡眠3s。

通过计时来观察,程序执行的过程,和Parallel的执行。

public void ParallelInvokeMethod()
{
    sp.Start();
    Parallel.Invoke(Run1, Run2);
    sp.Stop();
    Console.WriteLine("Parallel run " + sp.ElapsedMilliseconds + " ms.");

    sp.Restart();
    Run1();
    Run2();
    sp.Stop();
    Console.WriteLine("Normal run " + sp.ElapsedMilliseconds + " ms.");
}

实例化该方法并在mian函数中调用执行,可以从结果中观察到,该方法两段程序的执行结果的差异。

很直观的看出,使用Parallel.Invoke()之后,Run1和Run2是并行执行的,一共用时3s(3000ms左右),而直接运行Run1和Run2则耗时5s。

Parallel.For()

Parallel.For()的用法和 For 类似,直接看代码:

public void ParallelForMethod()
{
    Stopwatch sp = new Stopwatch();

    sp.Start();

    for (int i = 0; i < 10000; i++)
    {
        for (int j = 0; j < 60000; j++)
        {
            int sum = 0;
            sum+=i;
        }
    }
    sp.Stop();
    Console.WriteLine("NormalFor run " + sp.ElapsedMilliseconds + " ms.");

    sp.Reset();
    sp.Start();
    Parallel.For(0, 10000, item =>
                 {
                     for (int j = 0; j < 60000; j++)
                     {
                         int sum = 0;
                         sum += item;
                     }
                 });
    sp.Stop();
    Console.WriteLine("ParallelFor run " + sp.ElapsedMilliseconds + " ms.");
}

一个使用Parallel.For的双层循环累加,和一个普通的双层循环累加,执行时间的差距却非常大。

Parallel.For实际上是并行执行了循环,因为内部只是一个单纯的累加,因此效率差异明显,但是并非所有的场景都适合使用并行循环

修改一下上面的方法。


public void ParallelForMethodFromGV()
{
    var obj = new Object();
    long num = 0;

    sp.Start();
    for (int i = 0; i < 10000; i++)
    {
        for (int j = 0; j < 60000; j++)
        {
            num++;
        }
    }
    sp.Stop();
    Console.WriteLine("NormalFor run " + sp.ElapsedMilliseconds + " ms.");

    sp.Reset();
    sp.Start();
    Parallel.For(0, 10000, item =>
    {
        for (int j = 0; j < 60000; j++)
        {
            lock (obj)
            {
                num++;
            }
        }
    });
    sp.Stop();
    Console.WriteLine("ParallelFor run " + sp.ElapsedMilliseconds + " ms.");

}

改为操作一个全局变量的累加,这个时候由于并行请求,需要等待调用内存中的全局变量num,效率反而降低。

同样的,由于并行处理的原因,For的结果并不是按照原有顺序进行的:

public void ParallelForCW()
{
    Parallel.For(0, 100, i => { Console.Write(i + "\t"); });
}

Parallel.For虽然在执行效率上高于For,但是必须要在符合条件的场景下使用!

Parallel.ForEach()

ForEach是For的拓展,用于遍历数组或则list对象,实际上的意义和用法与For相同,因此Parallel中的 ForEach也是这般,这里不过多赘述。

Stopwatch sp = new Stopwatch();
public void ParallelForEachMethod()
{
    int[] arr = { 0,1,2,2,3,3,123,123,12,31,231,23,1,231,3,13,1,231,23,123,1,23,123,12,31,23,123,12,312,3,123,1,23};
    List<int> list = new List<int>(arr);
    List<int> list2 = new List<int>(arr);

    sp.Start();
    Parallel.ForEach(arr, item =>
    {
        list.Add(item);
    });
    sp.Stop();
    Console.WriteLine("ParallelForEach run " + sp.ElapsedMilliseconds + " ms.");

    sp.Restart();
    foreach (var item in arr)
    {
        list2.Add(item);
    }
    sp.Stop();
    Console.WriteLine("NormalForEach run " + sp.ElapsedMilliseconds + " ms.");
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从线程说起
  • 线程管理
  • 并行Parallel
    • Parallel.invoke()
      • Parallel.For()
        • Parallel.ForEach()
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档