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

简介: 原文:.NET 4 并行(多核)编程系列之二 从Task开始.NET 4 并行(多核)编程系列之二 从Task开始         前言:我们一步步的从简单的开始讲述,还是沿用我一直的方式:慢慢演化,步步为营。
原文: .NET 4 并行(多核)编程系列之二 从Task开始

.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交给底下的线程去执行。
    下面就看看代码:
   

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
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就是我们传入的参数。还是给大家举个例子:
   

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
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属性就可以获取结果。
    代码显示如下:
    

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
  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,并且获取结果:
    

img_405b18b4b6584ae338e0f6ecaf736533.gif 代码
   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

 

 

  





 



 



 

 









目录
相关文章
|
存储 JSON 开发工具
Visual Studio编程效率提升技巧集(提高.NET编程效率)
Visual Studio编程效率提升技巧集(提高.NET编程效率)
365 0
Visual Studio编程效率提升技巧集(提高.NET编程效率)
|
传感器 数据采集 物联网
探索.NET nanoFramework:为嵌入式设备编程的新途径
探索.NET nanoFramework:为嵌入式设备编程的新途
870 7
|
大数据 开发工具 开发者
从零到英雄:.NET核心技术带你踏上编程之旅,构建首个应用,开启你的数字世界探险!
【8月更文挑战第28天】本文带领读者从零开始,使用强大的.NET平台搭建首个控制台应用。无论你是新手还是希望扩展技能的开发者,都能通过本文逐步掌握.NET的核心技术。从环境搭建到创建项目,再到编写和运行代码,详细步骤助你轻松上手。通过计算两数之和的小项目,你不仅能快速入门,还能为未来开发更复杂的应用奠定基础。希望本文为你的.NET学习之旅开启新篇章!
220 1
|
存储 C#
揭秘C#.Net编程秘宝:结构体类型Struct,让你的数据结构秒变高效战斗机,编程界的新星就是你!
【8月更文挑战第4天】在C#编程中,结构体(`struct`)是一种整合多种数据类型的复合数据类型。与类不同,结构体是值类型,意味着数据被直接复制而非引用。这使其适合表示小型、固定的数据结构如点坐标。结构体默认私有成员且不可变,除非明确指定。通过`struct`关键字定义,可以包含字段、构造函数及方法。例如,定义一个表示二维点的结构体,并实现计算距离原点的方法。使用时如同普通类型,可通过实例化并调用其成员。设计时推荐保持结构体不可变以避免副作用,并注意装箱拆箱可能导致的性能影响。掌握结构体有助于构建高效的应用程序。
717 7
.NET 4.0下实现.NET4.5的Task类相似功能组件
【10月更文挑战第29天】在.NET 4.0 环境下,可以使用 `BackgroundWorker` 类来实现类似于 .NET 4.5 中 `Task` 类的功能。`BackgroundWorker` 允许在后台执行耗时操作,同时不会阻塞用户界面线程,并支持进度报告和取消操作。尽管它有一些局限性,如复杂的事件处理模型和不灵活的任务管理方式,但在某些情况下仍能有效替代 `Task` 类。
279 0
|
Java Spring 自然语言处理
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
337 0
|
传感器 数据采集 物联网
探索未来:.NET nanoFramework引领嵌入式设备编程革新之旅
【8月更文挑战第28天】.NET nanoFramework 是一款专为资源受限的嵌入式设备设计的轻量级、高性能框架,基于 .NET Core,采用 C# 进行开发,简化了传统底层硬件操作的复杂性,极大提升了开发效率。开发者可通过 Visual Studio 或 Visual Studio Code 快速搭建环境并创建项目,利用丰富的库和驱动程序轻松实现从基础 LED 控制到网络通信等多种功能,显著降低了嵌入式开发的门槛。
490 0
|
JSON 编解码 Go
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第25天】Go语言`net/http`包提供HTTP客户端和服务器功能,简化高性能网络应用开发。本文探讨如何发起HTTP请求,常见问题及解决策略。示例展示GET和POST请求的实现。注意响应体关闭、错误处理、内容类型设置、超时管理和并发控制。最佳实践包括重用`http.Client`,使用`context.Context`,处理JSON以及记录错误日志。通过实践这些技巧,提升HTTP编程技能。
407 1
|
Go 开发者
Golang深入浅出之-HTTP客户端编程:使用net/http包发起请求
【4月更文挑战第24天】Go语言的`net/http`包在HTTP客户端编程中扮演重要角色,但使用时需注意几个常见问题:1) 检查HTTP状态码以确保请求成功;2) 记得关闭响应体以防止资源泄漏;3) 设置超时限制,避免长时间等待;4) 根据需求处理重定向。理解这些细节能提升HTTP客户端编程的效率和质量。
361 1
|
SQL 开发框架 安全
【.NET Core】深入理解任务并行库 (TPL)
【.NET Core】深入理解任务并行库 (TPL)
314 0