• [转].NET 4 并行(多核)编程系列之二 从Task开始


    本文转自:http://www.cnblogs.com/yanyangtian/archive/2010/05/22/1741379.html

    .NET 4 并行(多核)编程系列之二 从Task开始

             前言:我们一步步的从简单的开始讲述,还是沿用我一直的方式:慢慢演化,步步为营。
        本篇文章的议题如下:     1.Task基础介绍     2.Task的创建

        3.获取Task的执行结果

      4. 补充细节

      系列文章链接:

      .NET 4 并行(多核)编程系列之一入门介绍

      .NET 4 并行(多核)编程系列之二 从Task开始 

      .NET 4 并行(多核)编程系列之三 从Task的取消 

      .NET 4 并行(多核)编程系列之四 Task的休眠 

      .NET 并行(多核)编程系列之五 Task执行和异常处理 

      .NET 并行(多核)编程系列之六 Task基础部分完结篇 

      .NET 并行(多核)编程系列之七 共享数据问题和解决概述

        1.Task基础介绍

        首先我们还是来看看一段简单的代码:    

        这里展示的只是一段简单的代码,不能显示出并行编程的特点。但是我们还是从最基本的开始看,慢慢进入深一点的话题。          如果你曾经用过.NET 中的多线程编程,比较一下,就会发现:这段代码虽然在底层还是使用了多线程,但是写法上却简化了很多,一行代码就实现了一个并行编程。          下面我们就从Task类开始谈。     Task类是Task Programming Library(TPL)中最核心的一个类,下面我将会像大家展示如何使用一些方法来创建不同类型的Task, 取消Task,等待Task执行完成,获取Task执行后的结果和对异常进行处理。
        在开始讨论之前,我们首先快速的看看之前的代码:        这个命名空间将会是我们之后在讲述并行编程经常使用的一个。这个空间包含了很多与并行编程有关的类。
        还有一个要你使用的命名空间是:System.Threading,大家对这个应该比较熟悉了,之前的多线程编程常常使用到,这个空间下包含了一些在并行编程中用来协调数据的一些类。
        上面代码中,最主要的代码如下:    

    Task.Factory.StartNew(() =>
                {
                    Console.WriteLine("Hello World");
                });

        我们用静态方法:Task.Factory.StartNew()来创建了一个最简单的Task--在屏幕上打印一句话。这段代码确实简单,而且都没有任何输入和需要返回的结果。
        下面我们就正式进入议题:     

    2.Task的创建   

           如果只是创建一个简单的Task,我们只要为该Task提供一个执行体就行了,执行体可以是一个委托delegate或者action。我们之前展示的那段代码就是采用了lambda表达式来作为Task的执行体。
        2.1 创建一个简单的Task

        为了执行一个简单的Task,一般进行以下步骤:     首先,要创建一个Task类的实例,     然后,传入一个System.Action委托,这个委托中的方法就是这个Task运行时你要执行的方法,而且这个委托必须作为Task构造函数的一个参数传入。我们在传入委托作为参数的时候有多种方式:传入匿名委托, Lambda表达式或者一个显示什么方法的委托。     最后,调用Task实例的Start()方法来运行。          当这个Task实例开始运行的时候,它就被传给了内部的一个task scheduler,这个scheduler负责把我们创建的task交给底下的线程去执行。     下面就看看代码:    

    using System;
    using System.Threading.Tasks;
    namespace Listing_02
    {
        class Listing_02
        {
            static void Main(string[] args)
            {
    
                // use an Action delegate and a named method
                Task task1 = new Task(new Action(printMessage));
                // use a anonymous delegate
                Task task2 = new Task(delegate
                {
                    printMessage();
                });
    
                // use a lambda expression and a named method
                Task task3 = new Task(() => printMessage());
                // use a lambda expression and an anonymous method
                Task task4 = new Task(() =>
                {
                    printMessage();
                });
    
                task1.Start();
                task2.Start();
                task3.Start();
                task4.Start();
                // wait for input before exiting
                Console.WriteLine("Main method complete. Press enter to finish.");
                Console.ReadLine();
            }
    
            static void printMessage()
            {
                Console.WriteLine("Hello World");
            }
        }
    }

        不知道大家注意到了没有,上面代码创建Task的方法和我们之前的第一段代码的创建Task的方法不同。在之前我们采用的是Task.Factory.StartNew()方法来创建的,这个方法创建Task并且开始运行Task,其实两端代码的结果是一样的,这里给出一点建议:如果这是想简单的创建一个Task,那么使用Factory.NewStart()来创建,很简便,如果像对所创建的Task附加更多的定制和设置特定的属性,那么还是得一步一步的按照我们说的那些步骤来。(详细的我们后续会介绍的)
        2.1 为创建的Task传入参数  

           我们之前提过,在创建Task的时候,我们在构造函数中传入了一个System.Action的委托,如果我们想要把一些参数传入到Task中,那么我 们可以传入System.Action<object>的委托,其中的那个object就是我们传入的参数。还是给大家举个例子:    

    using System;
    using System.Threading.Tasks;
    namespace Listing_04
    {
        class Listing_04
        {
            static void Main(string[] args)
            {
                string[] messages = { "First task", "Second task",
    "Third task", "Fourth task" };
                foreach (string msg in messages)
                {
                    Task myTask = new Task(obj => printMessage((string)obj), msg);
                    myTask.Start();
                }
                // wait for input before exiting
                Console.WriteLine("Main method complete. Press enter to finish.");
                Console.ReadLine();
            }
    
            static void printMessage(string message)
            {
                Console.WriteLine("Message: {0}", message);
            }
        }
    }

             注意:我们在传入参数后,必须把参数转换为它们原来的类型,然后再去调用相应的方法。例子中,因为System.Action对应的方法是printMessage()方法,而这个方法的要求的参数类型是string,所以要转换为string。
      想向Task传入参素,只能用System.Action<object>
        3.获取Task的执行结果      

       如果要获取Task的结果,那么在创建Task的时候,就要采用Task<T>来实例化一个Task,其中的那个T就是task执行完成之后返回结果的类型。之后采用Task实例的Result属性就可以获取结果。     代码显示如下:     

    static void Main(string[] args)
            {
                // create the task
                Task<int> task1 = new Task<int>(() =>
                {
                    int sum = 0;
                    for (int i = 0; i < 100; i++)
                    {
                        sum += i;
                    }
                    return sum;
                });
                
                task1.Start();
                // write out the result
                Console.WriteLine("Result 1: {0}", task1.Result);
                
                Console.ReadLine();
            }

        只有在task执行完成之后,才能获取到Result的值。
        下面的代码展示了如何通过Task.Factory.StartNew<T>()创建一个Task,并且获取结果:     

      static void Main(string[] args)
            {
                // create the task
                Task<int> task1 = Task.Factory.StartNew<int>(() =>
                {
                    int sum = 0;
                    for (int i = 0; i < 100; i++)
                    {
                        sum += i;
                    }
                    return sum;
                });
    
                // write out the result
                Console.WriteLine("Result 1: {0}", task1.Result);
                
                Console.ReadLine();
            }

      4. 补充细节   

      在创建Task的时候,Task有很多的构造函数的重载,一个主要的重载就是传入TaskCreateOptions的枚举:          TaskCreateOptions.None:用默认的方式创建一个Task     TaskCreateOptions.PreferFairness:请求scheduler尽量公平的执行Task(后续文章会将是,Task和线程一样,有优先级的)     TaskCreateOptions.LongRunning:声明Task将会长时间的运行。     TaskCreateOptions.AttachToParent:因为Task是可以嵌套的,所以这个枚举就是把一个子task附加到一个父task中。
        最后要提到的一点就是,我们可以在Task的执行体中用Task.CurrentId来返回Task的唯一表示ID(int)。如果在Task执行体外使用这个属性就会得到null。
        (熟悉WF的朋友,可以比较Task和WF的一些区别,因为我认为它们的设计思想很相近)
        今天就到这里了,多谢大家! 

      版权为小洋和博客园所有,转载请标明出处给作者。

      http://www.cnblogs.com/yanyangtian

  • 相关阅读:
    django页面分类和继承
    django前端从数据库获取请求参数
    pycharm配置django工程
    django 应用各个py文件代码
    CF. 1428G2. Lucky Numbers(背包DP 二进制优化 贪心)
    HDU. 6566. The Hanged Man(树形背包DP DFS序 重链剖分)
    小米邀请赛 决赛. B. Rikka with Maximum Segment Sum(分治 决策单调性)
    区间树 学习笔记
    CF GYM. 102861M. Machine Gun(主席树)
    2016-2017 ACM-ICPC East Central North America Regional Contest (ECNA 2016) (B, D, G, H)
  • 原文地址:https://www.cnblogs.com/freeliver54/p/4450589.html
Copyright © 2020-2023  润新知