C#并行编程中的Parallel.Invoke

简介:

一、基础知识

     并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴。所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务,这些任务不考虑互相的依赖和顺序。这样我们就可以使用很好的使用并行编程。但是我们都知道多核处理器的并行设计使用共享内存,如果没有考虑并发问题,就会有很多异常和达不到我们预期的效果。不过还好NET Framework4.0引入了Task Parallel Library(TPL)实现了基于任务设计而不用处理重复复杂的线程的并行开发框架。它支持数据并行,任务并行与流水线。核心主要是Task,但是一般简单的并行我们可以利用Parallel提供的静态类如下三个方法。

         Parallel.Invoke  对给定任务实现并行开发

         Parallel.For  对固定数目的任务提供循环迭代并行开发

         parallel.Foreach 对固定数目的任务提供循环迭代并行开发

注意:所有的并行开发不是简单的以为只要将For或者Foreach换成Parallel.For与Parallel.Foreach这样简单。

PS:从简单的Invoke开始逐步深入探讨并行开发的主要知识点,也对自己学习过程中的积累做个总结,其中参考了博客园中的其他优秀博文

      滴答的雨 异步编程:轻量级线程同步基元对象

      首先感谢您,在我学习并行开发过程中,您的博文对我帮助很大。

二、Parallel.Invoke在并行中的使用

      首先我们来看看它的两个重载方法:    

  public static void Invoke(params Action[] actions);  
  public static void Invoke(ParallelOptions parallelOptions, params Action[] actions);

 

       Invoke主要接受params的委托actions,比如我们要同时执行三个任务,我们可以这样利用     

复制代码
复制代码
  方式一
Parallel.Invoke(() => Task1(), () => Task2(), () => Task3());
方式二
Parallel.Invoke(Task1, Task2, Task3);
方式三
Parallel.Invoke(
                () =>
                {
                    Task1();
                },
               Task2,
                delegate () { Task3(); console.write('do someting!');});
复制代码
复制代码

这样Invoke就简单实现了Task1,Task2,Task3的并行开发。下面我们用实例来说明他们的执行规则。以及两个重载方法的使用。

三 、Demo

     1、 Demo 1:

复制代码
复制代码
public class ParallelInvoke
    {
        /// <summary>
        /// Invoke方式一 action
        /// </summary>
        public  void Client1()
        {
            Stopwatch stopWatch = new Stopwatch();
           
            Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
            stopWatch.Start();
            Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"));
            stopWatch.Stop();
            Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
        }

        private void Task1(string data)
        {
            Thread.Sleep(5000);
            Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
        }

        private void Task2(string data)
        {
            Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
        }

        private void Task3(string data)
        {
            Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
        }
}
复制代码
复制代码

执行运行后结果:

我们看到Invoke 执行Task三个方法主要有以下几个特点:

      1、没有固定的顺序,每个Task可能是不同的线程去执行,也可能是相同的;

      2、主线程必须等Invoke中的所有方法执行完成后返回才继续向下执行;这样对我们以后设计并行的时候,要考虑每个Task任务尽可能差不多,如果相差很大,比如一个时间非常长,其他都比较短,这样一个线程可能会影响整个任务的性能。这点非常重要

      3、这个非常简单就实现了并行,不用我们考虑线程问题。主要Framework已经为我们控制好线程池的问题。

ps:如果其中有一个异常怎么办? 带做这个问题修改了增加了一个Task4.

    2、 Demo2  

 

复制代码
复制代码
    public class ParallelInvoke
    {
        /// <summary>
        /// Invoke方式一 action
        /// </summary>
        public  void Client1()
        {
            Stopwatch stopWatch = new Stopwatch();
           
            Console.WriteLine("主线程:{0}线程ID : {1};开始", "Client1", Thread.CurrentThread.ManagedThreadId);
            stopWatch.Start();

            try
            {
                Parallel.Invoke(() => Task1("task1"), () => Task2("task2"), () => Task3("task3"), delegate () { throw new Exception("我这里发送了异常"); });
            }
            catch (AggregateException ae)
            {
                foreach (var ex in ae.InnerExceptions)
                    Console.WriteLine(ex.Message);
            }
          
            stopWatch.Stop();
            Console.WriteLine("主线程:{0}线程ID : {1};结束,共用时{2}ms", "Client1", Thread.CurrentThread.ManagedThreadId, stopWatch.ElapsedMilliseconds);
        }
   }
复制代码
复制代码

 

 

 

主要看 delegate() { throw new Exception("我这里发送了异常");} 增加了这个委托Task3. 然后我们看结果:

    这里我们发现即使有异常程序也会完成执行,而且不会影响其他Task的执行。

    3、demo3 重载方法ParallelOptions 的使用。

   理解ParallelOptions建议大家异步编程:轻量级线程同步基元对象   讲的非常详细。

   主要理解两个参数:

     CancellationToken     控制线程的取消
     MaxDegreeOfParallelism  设置最大的线程数,有时候可能会跑遍所有的内核,为了提高其他应用程序的稳定性,就要限制参与的内核

     下面从代码上看效果如何?

复制代码
复制代码
 public class ParallelInvoke
    {
     // 定义CancellationTokenSource 控制取消
        readonly CancellationTokenSource _cts = new CancellationTokenSource();

        /// <summary>
        /// Invoke方式一 action
        /// </summary>
        public  void Client1()
        {
             Console.WriteLine("主线程:{0}线程ID : {1};开始{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
            
            var po = new ParallelOptions
            {
                CancellationToken = _cts.Token, // 控制线程取消
                MaxDegreeOfParallelism = 3  // 设置最大的线程数3,仔细观察线程ID变化
            };
           
            Parallel.Invoke(po, () => Task1("task1"), ()=>Task5(po), Task6);
            
            Console.WriteLine("主线程:{0}线程ID : {1};结束{2}", "Client3", Thread.CurrentThread.ManagedThreadId, DateTime.Now);
        }

        private void Task1(string data)
        {
            Thread.Sleep(5000);
            Console.WriteLine("任务名:{0}线程ID : {1}", data, Thread.CurrentThread.ManagedThreadId);
        }

// 打印数字 private void Task5(ParallelOptions po) { Console.WriteLine("进入Task5线程ID : {0}", Thread.CurrentThread.ManagedThreadId); int i = 0; while (i < 100) { // 判断是否已经取消 if (po.CancellationToken.IsCancellationRequested) { Console.WriteLine("已经被取消。"); return; } Thread.Sleep(100); Console.Write(i + " "); Interlocked.Increment(ref i); } } /// <summary> /// 10秒后取消 /// </summary> private void Task6() { Console.WriteLine("进入取消任务,Task6线程ID : {0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(1000 * 10); _cts.Cancel(); Console.WriteLine("发起取消请求..........."); } }
复制代码
复制代码

执行结果:

     从程序结果我们看到以下特点:

            1、程序在执行过程中线程数码不超过3个。

            2、CancellationTokenSource/CancellationToken控制任务的取消。

四、总结

      Parallel.Invoke 的使用过程中我们要注意以下特点:

      1、没有特定的顺序,Invoke中的方法全部执行完才返回,但是即使有异常在执行过程中也同样会完成,他只是一个很简单的并行处理方法,特点就是简单,不需要我们考虑线程的问题。

      2、如果在设计Invoke中有个需要很长时间,这样会影响整个Invoke的效率和性能,这个我们在设计每个task时候必须去考虑的。

      3、Invoke 参数是委托方法。

      4、当然Invoke在每次调用都有开销的,不一定并行一定比串行好,要根据实际情况,内核环境多次测试调优才可以。

      5、异常处理比较复杂。


本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/6015996.html,如需转载请自行联系原作者

相关文章
|
1天前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
|
1天前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
|
1天前
|
存储 安全 网络安全
C#编程的安全性与加密技术
【4月更文挑战第21天】C#在.NET框架支持下,以其面向对象和高级特性成为安全软件开发的利器。本文探讨C#在安全加密领域的应用,包括使用System.Security.Cryptography库实现加密算法,利用SSL/TLS保障网络传输安全,进行身份验证,并强调编写安全代码的重要性。实际案例涵盖在线支付、企业应用和文件加密,展示了C#在应对安全挑战的同时,不断拓展其在该领域的潜力和未来前景。
|
1天前
|
人工智能 C# 开发者
C#编程中的图形界面设计
【4月更文挑战第21天】本文探讨了C#在GUI设计中的应用,介绍了Windows Forms、WPF和UWP等常用框架,强调了简洁界面、响应式设计和数据绑定等最佳实践。通过实际案例,展示了C#在企业应用、游戏开发和移动应用中的GUI实现。随着技术发展,C#在GUI设计的未来将趋向于跨平台、更丰富的组件和AI集成,为开发者创造更多可能性。
|
1天前
|
存储 算法 C#
C#编程与数据结构的结合
【4月更文挑战第21天】本文探讨了C#如何结合数据结构以构建高效软件,强调数据结构在C#中的重要性。C#作为面向对象的编程语言,提供内置数据结构如List、Array和Dictionary,同时也支持自定义数据结构。文章列举了C#实现数组、链表、栈、队列等基础数据结构的示例,并讨论了它们在排序、图算法和数据库访问等场景的应用。掌握C#数据结构有助于编写高性能、可维护的代码。
|
1天前
|
开发框架 Linux C#
C#编程的跨平台应用
【4月更文挑战第21天】C#与.NET Core的结合使得跨平台应用开发变得高效便捷,提供统一编程模型和高性能。丰富的类库、活跃的社区支持及Visual Studio Code、Xamarin等工具强化了其优势。广泛应用在企业系统、云服务和游戏开发中,虽面临挑战,但随着技术进步,C#在跨平台开发领域的前景广阔。
|
1天前
|
人工智能 C# 云计算
C#编程的未来发展趋向
【4月更文挑战第21天】C#编程未来将深化跨平台支持,强化云计算与容器技术集成,如.NET Core、Docker。在AI和ML领域,C#将提供更丰富框架,与AI芯片集成。语言和工具将持续创新,优化异步编程,如Task、async和await,提升多核性能。开源生态的壮大将吸引更多开发者,共创更多机遇。
|
1天前
|
程序员 C#
C#编程中的面向对象编程思想
【4月更文挑战第21天】本文探讨了C#中的面向对象编程,包括类、对象、封装、继承和多态。类是对象的抽象,定义属性和行为;对象是类的实例。封装隐藏内部细节,只暴露必要接口。继承允许类复用和扩展属性与行为,而多态使不同类的对象能通过相同接口调用方法。C#通过访问修饰符实现封装,使用虚方法和抽象方法实现多态。理解并应用这些概念,能提升代码的清晰度和可扩展性,助你成为更好的C#程序员。
|
1天前
|
开发框架 安全 .NET
C#编程高手的成长之路
【4月更文挑战第21天】本文揭示了成为C#编程高手的路径:牢固掌握基础知识和面向对象编程,深入了解C#特性如泛型和委托,精通ASP.NET等框架工具,养成良好编程习惯,持续学习实践并参与开源项目,勇于挑战创新。通过这些步骤,不断提升编程技能,迈向C#编程的巅峰。
|
1天前
|
IDE 程序员 C#
C#编程入门:从零开始的旅程
【4月更文挑战第20天】本文引导初学者入门C#编程,从环境搭建开始,推荐使用Visual Studio Community版作为IDE。接着,通过编写&quot;Hello, World!&quot;程序,介绍基本语法,包括数据类型、运算符和表达式。文章还涉及控制结构、函数和方法,以及面向对象编程概念。通过学习,读者将对C#有初步了解,并激发进一步探索编程世界的兴趣。