• [深入学习C#]C#实现多线程的方式:使用Parallel类


    简介

      在C#中实现多线程的另一个方式是使用Parallel类。 
      在.NET4中 ,另一个新增的抽象线程是Parallel类 。这个类定义了并行的for和foreach的 静态方法。在为 for和 foreach定 义的语言中,循环从一个线程中运行 。Parallel类使用多个任务,因此使用多个线程来完成这个作业。 
      我们在前文中,对任务作出了一定的阐释,有兴趣的朋友可以前去查看。 
      Parallel.For()和 Parallel.ForEach()方法多次调用同一个方法,而 Parallel.Invoke()方法允许同时调用不同的方法。

    使用Parallel.For()方法

      基本使用方法

      Parallel.For()方法类似于 C#的 for循环语旬,也是多次执行一个任务。使用Parallel.For()方法,可以并行运行迭代。 迭代的顺序没有定义。 
      在For()方法中,前两个参数定义了循环的开头和结束。示例从0迭代到 9。第 3个参数是一个Action<int>委托。 整数参数是循环的迭代次数,该 参数被传递给Action<int>委托引用的方法。Parallel.For()方法的返回类型是ParalleLoopResult结构,它提供了循环是否结束的信息。

    ParallelLoopResult result = Parallel.For(0, 10, i =>
                {
                    Console.WriteLine("{0}, task : {1}, thread : {2}", i, Task.CurrentId, Thread.CurrentThread.ManagedThreadId);
                });
                Console.WriteLine(result.IsCompleted);
                Console.ReadKey();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      在Parallel.For()的方法体中,把索引、任务标识符和线程标识符写入控制台中。从下面的输出截图可以看出,由于每次循环都开启了新的任务和线程,因此每个线程的执行顺序是不能保证的。 
      这里写图片描述这里写图片描述

      中断循环

      同For()循环类似,Parallel.For()方法也可以中断循环的执行。 
      Parallel.For()方法的一个重载版本接受第3个Action<int, ParallelLoopState>类型的参数。使用这些参数定义一个方法,就可以调用ParalleLoopState的Break()或Stop()方法,以影响循环的结果。 
      注意,迭代的顺序没有定义。

     ParallelLoopResult result = Parallel.For(0, 10, (int i, ParallelLoopState pls) =>
                {
                    Console.WriteLine("i: {0}, task : {1}", i, Task.CurrentId);
                    Thread.Sleep(10);
                    if (i > 15)
                    {
                        pls.Break();
                    }
                });
                Console.WriteLine(result.IsCompleted);
                Console.WriteLine("Lowest break iteration: {0}", result.LowestBreakIteration);
                Console.ReadKey();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      下面是结果截图: 
      这里写图片描述这里写图片描述 
       
      应用程序这次的运行说明,迭代在值大于15时中断,但其他任务可以同时运行,有其他值的任务也可以运行。利用LowestBreakIteration属性,可以忽略其他任务的结果。

      Parallel.For<TLocal>方法

      Parallel.For()方法可能使用几个线程来执行循环 。如果需要对每个线程进行初始化,就可以使用Parallel.For<TLocal>方法。除了from和to对应的值之外,For()方法的泛型版本还接受3个委托参数。 
      第一个参数的类型是Func<TLocal> ,因为这里的例子对于TLocal使用字符串,所以该方法需要定义为Func<string>,即返回string的方法。这个方法仅对于用于执行迭代的每个线程调用一次。 
      第二个委托参数为循环体定义了委托。在示例中,该参数的类型是Func<int, ParallelLoopState, string, string>。 其中第一个参数是循环迭代,第二个参数 ParallelLoopstate允许停止循环,如前所述 。循环体方法通过第3个参数接收从init方法返回的值,循环体方法还需要返回一个值,其类型是用泛型for参数定义的。 
      For()方法的最后一个参数指定一个委托Action<TLocal>;在该示例中,接收一个字符串。 这个方法仅对于每个线程调用一次,这是一个线程退出方法。

    Parallel.For<string>(0, 20,
                    () =>
                    {
                        Console.WriteLine("init thread {0},	 task {1}", Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                        return string.Format("t{0}", Thread.CurrentThread.ManagedThreadId);
                    },
                (i, pls, str) =>
                {
                    Console.WriteLine("body i {0} 	 str {1} 	 thread {2} 	 task {3}", i, str, Thread.CurrentThread.ManagedThreadId, Task.CurrentId);
                    Thread.Sleep(10);
                    return string.Format("i 	{0}", i);
                },
                (str) =>
                {
                    Console.WriteLine("finally	 {0}", str);
                });
                Console.ReadKey();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      程序运行结果: 
    这里写图片描述这里写图片描述

      备注: 
      Parallel.For<TLocal> 方法 (Int32, Int32, Func<TLocal>, Func<Int32, ParallelLoopState, TLocal, TLocal>, Action<TLocal>
      类型参数 
      TLoca 
      线程本地数据的类型。

      参数

      fromInclusive 
      类型:System.Int32 
      开始索引(含)。

      toExclusive 
      类型:System.Int32 
      结束索引(不含)。

      localInit 
      类型:System.Func<TLocal> 
      用于返回每个任务的本地数据的初始状态的函数委托。

      body 
      类型:System.Func<Int32, ParallelLoopState, TLocal, TLocal> 
      将为每个迭代调用一次的委托。

      localFinally 
      类型:System.Action<TLocal> 
      用于对每个任务的本地状态执行一个最终操作的委托。

      返回值 
      类型:System.Threading.Tasks.ParallelLoopResult 
       
      在迭代范围 (fromInclusive,toExclusive) ,为每个值调用一次body 委托。为它提供以下参数:迭代次数 (Int32)、可用来提前退出循环的ParallelLoopState实例以及可以在同一线程上执行的迭代之间共享的某些本地状态。

      对于参与循环执行的每个任务调用 localInit 委托一次,并返回每个任务的初始本地状态。 这些初始状态传递给第一个在该任务上 调用的 body。 然后,每个后续正文调用返回可能修改过的状态值,传递到下一个正文调用。 最后,每个任务上的最后正文调用返回传递给 localFinally 委托的状态值。 每个任务调用 localFinally 委托一次,以对每个任务的本地状态执行最终操作。此委托可以被多个任务同步调用;因此您必须同步对任何共享变量的访问。

      Parallel.For方法比在它执行生存期的线程可能使用更多任务,作为现有的任务完成并被新任务替换。 这使基础 TaskScheduler 对象有机会添加、更改或移除服务循环的线程。

      如果 fromInclusive 大于或等于 toExclusive,则该方法立即返回,而无需执行任何迭代。

    使用Parallel.ForEach()方法

      基本使用方法

      Parallel.ForEach()方法遍历实现了IEnumerable的集合,其方式类似于foreach语句,但以异步方式遍历。这里也没有确定遍历顺序。 
      

    string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
                ParallelLoopResult result = Parallel.ForEach<string>(data, (s) =>
                {
                    Console.WriteLine(s);
                });
                Console.ReadKey();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      结果截图: 
    这里写图片描述这里写图片描述

      中断循环

      如果需要中断循环,就可以使用ForEach()方法的重载版本和ParallelLoopState参数。其方式与前面的For()方法相同。ForEach()方法的一个重载版本也可以用于访问索引器,从而获得迭代次数,如下所示: 

    string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve" };
                ParallelLoopResult result = Parallel.ForEach<string>(data, (s, pls, l) =>
                 {
                     Console.WriteLine("{0}	{1}", s, l);
                     if (l > 10)
                     {
                         pls.Break();
                     }
                 });
                Console.WriteLine("Lowest break iteration: {0}", result.LowestBreakIteration);
                Console.ReadKey();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

      结果截图: 
    这里写图片描述这里写图片描述

    使用Parallel.Invoke()方法

      如果多个任务应并行运行,就可以使用Parallel.Invoke()方法。Parallel.Invoke()方法允许传递一个Action委托数组,在其中可以指定应运行的方法。 
      示例代码传递了要并行调用的Foo()和Bar()方法: 
      

            static void Main(string[] args)
            {
                Parallel.Invoke(Foo, Bar);
                Console.ReadKey();
            }
            static void Foo()
            {
                Console.WriteLine("Foo");
            }
    
            static void Bar()
            {
                Console.WriteLine("Bar");
            }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

      结果截图: 
      这里写图片描述

  • 相关阅读:
    WinForm:实现类似QQ消息框一样的右下角消息提示窗口
    WinForm:系统托盘NotifyIcon
    多线程学习系列:(一)前言
    Codeforces Round #176 (Div. 2)总结A. IQ Test
    使用STL的next_permutation函数生成全排列(C++)
    c语言字符串 数字转换函数大全
    c语言中字符串处理函数
    杭电OJ题目分类
    Codeforces Beta Round #77 (Div. 2 Only)A. Football
    算法导论第三版目录
  • 原文地址:https://www.cnblogs.com/zxtceq/p/7842950.html
Copyright © 2020-2023  润新知