zoukankan      html  css  js  c++  java
  • C# 创建线程的多种方式之 Parallel类 基础知识

    Parallel 类定义了并行运行的静态For(), Foreach(), Invoke()方法, 其中For(), Foreach() 多次调用同一个方法,方法返回值均为ParallelLoopResult,而Invoke()可同时调用多个不同的方法,无返回值。

    For(), Foreach()方法有很多重载方法,以参数最多的For<TLocal>(Int32, Int32, ParallelOptions, Func<TLocal>, Func<Int32,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) 进行说明,其他类似。以下参数含义说明来自MSDN,

    类型参数

    TLocal:The type of the thread-local data.     //局部变量,在

    参数

    fromInclusive Int32  :The start index, inclusive.       //循环起始索引
    toExclusive Int32The end index, exclusive.           //循环结束索引
    parallelOptions ParallelOptionsAn object that configures the behavior of this operation.        //并行执行参数,包括最大并发数量,线程取消操作通知,任务调度器
    localInit Func<TLocal> :The function delegate that returns the initial state of the local data for each task.     //每个线程初始化函数的委托
    body Func<Int32,ParallelLoopState,TLocal,TLocal> :The delegate that is invoked once per iteration.           //循环运行的函数体,ParallelLoopState  类可以调用Stop()和Break()方法,停止循环运行
    localFinally Action<TLocal> :The delegate that performs a final action on the local state of each task.          //每个线程结束处理函数的委托
    为了方便观察,写了以下例子:
            static void Main(string[] args)
            {
                Console.WriteLine("Main Start....");
                ParallelOptions po = new ParallelOptions();      //并行执行参数类
                po.MaxDegreeOfParallelism = 4;        //最大并发数量
                int result=0;
                ParallelLoopResult pr = Parallel.For<int>(0, 20, po, () =>         //Initial
                {
                    Console.WriteLine("taskId {0} Parallel initial.....",Task.CurrentId);     //输出当前任务Id
                    return 1;
                },
                (i,ps,v1) =>                                                     //body
                {
                    v1 += 1;
                    Console.WriteLine("taskId {0} loopIndex{1},{2}", Task.CurrentId, i, v1);       //输出当前任务Id, 循环索引 , 局部变量
                    return v1;
                },
                (v1) =>                                                             //finally
                {               
                    Interlocked.Add(ref result, v1);                               //原子操作,操作共享变量result
                    Console.WriteLine("taskId {0} Parallel finalized.....Result is {1}", Task.CurrentId, v1);    //输出当前任务Id, 局部变量
                });
                Console.WriteLine("Main end....{0}",result);
                Console.ReadLine();
            }

    运行结果每次都有点不同,复制了一个运行结果,如下:

    taskId 1 Parallel initial.....
    taskId 1 loopIndex0,2
    taskId 1 loopIndex1,3
    taskId 2 Parallel initial.....
    taskId 2 loopIndex5,2
    taskId 2 loopIndex6,3
    taskId 4 Parallel initial.....
    taskId 4 loopIndex15,2
    taskId 4 loopIndex16,3
    taskId 4 loopIndex17,4
    taskId 4 loopIndex18,5
    taskId 4 loopIndex19,6
    taskId 4 loopIndex3,7
    taskId 4 loopIndex4,8
    taskId 4 loopIndex8,9
    taskId 4 loopIndex9,10
    taskId 4 loopIndex11,11
    taskId 4 loopIndex12,12
    taskId 4 loopIndex13,13
    taskId 4 loopIndex14,14
    taskId 3 Parallel initial.....
    taskId 1 loopIndex2,4
    taskId 1 Parallel finalized.....Result is 4
    taskId 3 loopIndex10,2
    taskId 3 Parallel finalized.....Result is 2
    taskId 2 loopIndex7,4
    taskId 2 Parallel finalized.....Result is 4
    taskId 4 Parallel finalized.....Result is 14
    Main end....24

    Paralle.For() 实际上是调用了线程池,至于调用线程的个数是不固定的,所以会导致每次运行显示不同。从上面的结果可以看出,此次运行共调用了4个线程,相应的 localInit 和 localFinally 也都调用了4次(注意: localInit 和 localFinally是指线程的初始化和结束处理,而不是循环)。从loopIndex的显示可以看出,循环执行是无顺序的。从局部变量v1的显示,可以看出局部变量在同一个线程中会由上次迭代传递给下次迭代,如上taskId 4 的局部变量一直在递增。

    循环体中止:body方法参数 ParallelLoopState  类,调用stop() 或 break() 即可,但这两中方式有些不同。

    Stop(): 停止当前迭代,阻止其他还未开始的迭代,已经在执行的迭代不受影响. 可以检测IsStop属性来判断是否调用Stop()方法,停止已经执行的迭代;   -- 推荐使用此方法停止

    Break(): 阻止当前其他高于当前循环索引的迭代,已经在执行的不受影响。其实可以在每次迭代中检测ShouldExitCurrentIteration 属性,若为真,说明其他迭代调用了Break(),紧接着马上检测LowestBreakIteration 属性,如果当前循环索引小于该值,则立马return。

    Parallel.Foreach() 的使用基本类似,ForEach<TSource,TLocal>(IEnumerable<TSource>, Func<TLocal>, Func<TSource,ParallelLoopState,TLocal,TLocal>, Action<TLocal>) ,

            static void Main(string[] args)
            {
                Console.WriteLine("Main Start....");
                List<int> numList = new List<int>();
                numList.AddRange(new int[]{0,1,2,3,4,5,6,7,8,9});
                int result=0;
                ParallelLoopResult pr = Parallel.ForEach<int, int>(numList, () =>         //Initial
                {
                    Console.WriteLine("taskId {0} Parallel initial.....",Task.CurrentId);     //输出当前任务Id
                    return 1;
                },
                (num,ps,i,v1) =>                                                     //body
                {
                    v1 += num;
                    Console.WriteLine("taskId {0} loopIndex{1},{2}", Task.CurrentId, i, v1);       //输出当前任务Id, 循环索引 , 局部变量
                    return v1;
                },
                (v1) =>                                                             //finally
                {               
                    Interlocked.Add(ref result, v1);                               //原子操作,操作共享变量result
                    Console.WriteLine("taskId {0} Parallel finalized.....Result is {1}", Task.CurrentId, v1);    //输出当前任务Id, 局部变量
                });
                Console.WriteLine("Main end....{0}",result);
                Console.ReadLine();
            }

    运行结果:

    Main Start....
    taskId 1 Parallel initial.....
    taskId 2 Parallel initial.....
    taskId 3 Parallel initial.....
    taskId 4 Parallel initial.....
    taskId 3 loopIndex4,5
    taskId 3 loopIndex5,10
    taskId 3 loopIndex7,17
    taskId 3 loopIndex8,25
    taskId 3 loopIndex9,34
    taskId 3 loopIndex1,35
    taskId 3 loopIndex3,38
    taskId 3 Parallel finalized.....Result is 38
    taskId 1 loopIndex0,1
    taskId 1 Parallel finalized.....Result is 1
    taskId 2 loopIndex2,3
    taskId 2 Parallel finalized.....Result is 3
    taskId 4 loopIndex6,7
    taskId 4 Parallel finalized.....Result is 7
    Main end....49

    从结果分析基本上与For() 类似,但Foreach() 里还有一个 Partitioner 参数, 分区器。 通过分区器可以控制并发线程的数目,以及每个线程运行的迭代次数。

    最后还有Invoke (params Action[] actions), 参数为params 可变参数,具体应用如下:

            static void Main(string[] args)
            {
                Console.WriteLine("Main Start....");
                Stopwatch sw = new Stopwatch();
                sw.Start();
                Parallel.Invoke(() =>
                {
                    Console.WriteLine("TaskId{0} Func1 runs...",Task.CurrentId);
                    Thread.Sleep(3000);
                },
                delegate
                {
                    Console.WriteLine("TaskId{0} Func2 runs...", Task.CurrentId);
                    Thread.Sleep(3000);
                },
                delegate
                {
                    Console.WriteLine("TaskId{0} Func3 runs...", Task.CurrentId);
                    Thread.Sleep(3000);
                });
                sw.Stop();
                Console.WriteLine("Main end....{0}",sw.Elapsed);
                Console.ReadLine();
            }

    运行结果:

    Main Start....
    TaskId1 Func1 runs...
    TaskId2 Func2 runs...
    TaskId3 Func3 runs...
    Main end....00:00:03.0503290

    从结果看出,调用了3个不同的线程同时执行,虽然每个方法都延迟了3000ms,但总共运行时间也差不多是3000ms。 

  • 相关阅读:
    LeetCode 326. Power of Three
    LeetCode 324. Wiggle Sort II
    LeetCode 322. Coin Change
    LeetCode 321. Create Maximum Number
    LeetCode 319. Bulb Switcher
    LeetCode 318. Maximum Product of Word Lengths
    LeetCode 310. Minimum Height Trees (DFS)
    个人站点大开发!--起始篇
    LeetCode 313. Super Ugly Number
    LeetCode 309. Best Time to Buy and Sell Stock with Cooldown (DP)
  • 原文地址:https://www.cnblogs.com/change-myself/p/11149392.html
Copyright © 2011-2022 走看看