`
pcajax
  • 浏览: 2162366 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

C# 使用Task执行异步操作

 
阅读更多

在C#4.0之前需要执行一个复杂的异步操作时,只能使用CLR线程池技术来执行一个任务。线程池执行异步任务时,不知道任务何时完成,以及任务的在任务完成后不能获取到返回值。但是在C#4.0中引人了一个的任务(System.Threading.Tasks命名空间的类型)机制来解决异步操作完成时间和完成后返回值的问题。

1.使用Task类创建并执行简单任务

    通过使用Task的构造函数来创建任务,并调用Start方法来启动任务并执行异步操作。创建任务时,必须传递一个Action或Action<Object>类型的委托回调方法,可以选择的传递任务执行时说需要的数据对象等。Task类的构造函数如下:

        public Task(Action action);
        public Task(Action<object> action, object state);
        public Task(Action action, CancellationToken cancellationToken);
        public Task(Action action, TaskCreationOptions creationOptions);
        public Task(Action<object> action, object state, CancellationToken cancellationToken);
        public Task(Action<object> action, object state, TaskCreationOptions creationOptions);
        public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions);
        public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions);

 

示例代码:

   1:  static void Main(string[] args)
   2:          {
   3:              Console.WriteLine("主线程执行业务处理.");
   4:              //创建任务
   5:              Task task = new Task(() => {
   6:                  Console.WriteLine("使用System.Threading.Tasks.Task执行异步操作.");
   7:                  for (int i = 0; i < 10; i++)
   8:                  {
   9:                      Console.WriteLine(i);
  10:                  }
  11:              });
  12:              //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
  13:              task.Start();
  14:              Console.WriteLine("主线程执行其他处理");
  15:              //主线程挂起1000毫秒,等待任务的完成。
  16:              Thread.Sleep(1000);
  17:          }

 

任务调度结果:image

2.等待任务的完成并获取返回值

     使用任务执行异步操作时,最主要的是要后的任务完成时的返回值。在任务类中有一个实例方法Wait(有许多重载版本)他能等待任务的完成,我们也可以通过Task类的派生类Task<TResult>创建一个异步任务,并指定任务完成时返回值的类型,这样可以通过Task<TResult>的实例对象获取到任务完成后的返回值。创建一个异步任务并执行0到100求和操作返回最后的计算结果,示例代码:

   1:  static void TaskWait() {
   2:              //创建任务
   3:              Task<int> task = new Task<int>(() =>
   4:              {
   5:                  int sum = 0;
   6:                  Console.WriteLine("使用Task执行异步操作.");
   7:                  for (int i = 0; i < 100; i++)
   8:                  {
   9:                      sum+=i;
  10:                  }
  11:                  return sum;
  12:              });
  13:              //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
  14:              task.Start();
  15:   
  16:              Console.WriteLine("主线程执行其他处理");
  17:              //等待任务的完成执行过程。
  18:              task.Wait();
  19:              //获得任务的执行结果
  20:              Console.WriteLine("任务执行结果:{0}", task.Result.ToString());
  21:  }

执行结果:image

Task类还有一些静态方法,WaitAll用于等待提供的所有 System.Threading.Tasks.Task 对象完成执行过程和Wait用于等待提供的任一个 System.Threading.Tasks.Task 对象完成执行过程,这两个方法都有一些重载版本。

//等待所有任务完成  
public static void WaitAll(params Task[] tasks);
//等待任意一个任务完成
public static int WaitAny(params Task[] tasks);

 

3.使用ContinueWith方法在任务完成时启动一个新任务

     在使用能够Task类的Wait方法等待一个任务时或派生类的Result属性获得任务执行结果都有可能阻塞线程,为了解决这个问题可以使用ContinueWith方法,他能在一个任务完成时自动启动一个新的任务来处理执行结果。

示例代码:

   1:  static void TaskContinueWith()
   2:          {
   3:              //创建一个任务
   4:              Task<int> task = new Task<int>(() =>
   5:              {
   6:                  int sum = 0;
   7:                  Console.WriteLine("使用Task执行异步操作.");
   8:                  for (int i = 0; i < 100; i++)
   9:                  {
  10:                      sum += i;
  11:                  }
  12:                  return sum;
  13:              });
  14:              //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
  15:              task.Start();
  16:              Console.WriteLine("主线程执行其他处理");
  17:              //任务完成时执行处理。
  18:              Task cwt = task.ContinueWith(t => { 
  19:                  Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString()); 
  20:              });
  21:              Thread.Sleep(1000);
  22:          }

 

执行结果:image

上述示例中任务不是等待完成来显示执行结果,而是使用ContinueWith方法,它能够知道任务在什么时候完成并启动一个新的任务来执行任务完成后的处理。ContinueWith方法具有一些重载版本,这些重载版本允许指定延续任务需要使用的数据、延续任务的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚举值按位OR运行的结果)等。

4.创建父子任务和任务工厂的使用

    通过Task类创建的任务是顶级任务,可以通过使用 TaskCreationOptions.AttachedToParent 标识把这些任务与创建他的任务相关联,所有子任务全部完成以后父任务才会结束操作。示例如下:

   1:  static void ParentChildTask() {
   2:              Task<string[]> parent = new Task<string[]>(state => {
   3:                  Console.WriteLine(state);
   4:                  string[] result=new string[2];
   5:                  //创建并启动子任务
   6:                  new Task(() => { result[0]= "我是子任务1。"; },TaskCreationOptions.AttachedToParent).Start();
   7:                  new Task(() => { result[1] = "我是子任务2。"; }, TaskCreationOptions.AttachedToParent).Start();
   8:                  return result;
   9:              },"我是父任务,并在我的处理过程中创建多个子任务,所有子任务完成以后我才会结束执行。");
  10:              //任务处理完成后执行的操作
  11:              parent.ContinueWith(t => {
  12:                  Array.ForEach(t.Result, r=>Console.WriteLine(r));
  13:              });
  14:              //启动父任务
  15:              parent.Start();
  16:              Console.Read();
  17:          }

 

执行结果:image

    如果需要创建一组具有相同状态的任务时,可以使用TaskFactory类或TaskFactory<TResult>类。这两个类创建一组任务时可以指定任务的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler默认值。示例代码:

   1:          static void TaskFactoryApply()
   2:          {
   3:              Task parent = new Task(() =>
   4:              {
   5:                  CancellationTokenSource cts = new CancellationTokenSource(5000);
   6:                  //创建任务工厂
   7:                  TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
   8:                  //添加一组具有相同状态的子任务
   9:                  Task[] task = new Task[]{
  10:                      tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第一个任务。"); }),
  11:                      tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第二个任务。"); }),
  12:                      tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第三个任务。"); })
  13:                  };
  14:              });
  15:              parent.Start();
  16:              Console.Read();
  17:          }

执行结果:image

5.任务内部实现和任务调度

    任务内部有一组构成任务状态的属性,标识任务的唯一Id、表示任务的执行状态(TaskStatus)、任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState、对任务创建时的任务调度对象(TaskScheduler)的引用、对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。Task类和Task<TResult>类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。可以使用Task类的CurrentId属性获得正在执行的任务的Id,如果没有任务在执行CurrentId返回值为null,CurrentId是一个int?可空类型的属性。任务执行的生命周期通过TaskStatus类型的一个值来表示,TaskStatus所包含的值:

public enum TaskStatus
        {
            Created = 0,
            WaitingForActivation = 1,
            WaitingToRun = 2,
            Running = 3,
            WaitingForChildrenToComplete = 4,
            RanToCompletion = 5,
            Canceled = 6,
            Faulted = 7,
        }

      我们可以通过Task类的Exception属性获得任务在执行过程中的所有异常 ,Exception是一个AggregateException类型的属性。Task类提供了IsCanceled、IsCompleted、IsFaulted属性来获得任务的完成状态。通过ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync创建的后续任务都处于WaitingForActivation 状态,这个状态的任务会在父任务完成后自动执行。

      在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类:ThreadPoolTaskScheduler线程池任务调度器和SynchronizationContextTaskScheduler同步上下文任务调度器。所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。SynchronizationContextTaskScheduler任务调度器能够用在Window form、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。

任务调度示例:

   1:    private void button1_Click(object sender, EventArgs e)
   2:          {
   3:               //获得同步上下文任务调度器
   4:             TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
   5:   
   6:              //创建任务,并采用默认任务调度器(线程池任务调度器)执行任务
   7:              Task<int> task = new Task<int>(() =>
   8:              {
   9:                  //执行复杂的计算任务。
  10:                  Thread.Sleep(2000);
  11:                  int sum = 0;
  12:                  for (int i = 0; i < 100; i++)
  13:                  {
  14:                      sum += i;
  15:                  }
  16:                  return sum;
  17:              });
  18:               var cts=new CancellationTokenSource();
  19:              //任务完成时启动一个后续任务,并采用同步上下文任务调度器调度任务更新UI组件。
  20:              task.ContinueWith(t => {this.label1.Text="采用SynchronizationContextTaskScheduler任务调度器更新UI。\r\n计算结果是:"+task.Result.ToString(); },
  21:                 cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler);
  22:              task.Start();
  23:          }

执行结果:image

    本文简单的介绍了使用Task类来执行异步操作以及任务的内部实现与任务调度。在执行复杂异步操作时,可以采用任务来执行,他能更好的知道异步操作在何时完成以及返回异步操作的执行结果。

分享到:
评论

相关推荐

    Task异步执行任务[C#源码]

    总之,`Task`异步执行任务是C#中进行高效率、低开销异步编程的关键工具,它简化了并发代码的编写,提高了程序的可读性和可维护性。通过深入理解并熟练运用`Task`,开发者能更好地应对现代计算环境中的性能挑战。

    C#实现异步连接Sql Server数据库的方法

    这是因为异步操作允许程序在等待数据库响应时执行其他任务,而不是阻塞主线程,从而避免了UI冻结或整体性能下降。本篇文章将深入探讨如何使用C#的`async`和`await`关键字来实现异步数据库连接。 首先,`async`和`...

    C# MVC TASK异步调用测试!

    假设了一个场景,用户在局域网Web服务器上下了一个单,程序将这个订单发送到远程服务器,然后用户付款到远程服务器, ...有可能用户下单之后就把本网站的下单页面给关闭...关键字:C# ,Asp.Net, MVC, TASK异步调用测试!

    C# Task任务操作代码

    在C#编程中,Task是并行编程的重要组成部分,它允许开发者在异步环境中执行操作,从而提高程序的性能和响应性。Task类是.NET Framework 4.0引入的,作为System.Threading.Tasks命名空间的一部分,它为编写多线程和...

    c#自定义异步操作数据库示例

    在C#编程中,异步操作是现代应用开发的关键特性,尤其在处理I/O密集型任务如数据库操作时,能够显著提升程序的响应速度。本文将深入探讨如何在C#中自定义异步操作来与数据库进行交互。 首先,我们要理解C#中的异步...

    c# 使用UDPClient实现异步通信的两种表现形式

    使用委托的方式更符合早期的异步编程模式,而Task-based异步模式(TAP)则是C# 5.0及更高版本推荐的异步编程模型,它使得异步代码更易于理解和维护,更符合人类的思维方式。根据项目需求和个人偏好,开发者可以选择...

    C#异步操作的一个例子

    C#中的异步操作主要基于.NET Framework的Task类和async/await关键字。Task代表一个异步操作,它可以是计算密集型的,也可以是I/O密集型的。async和await是C#自4.0版本引入的关键字,用于简化异步编程模型。async标记...

    winfrom 实现异步操作,C#实现异步

    当涉及到用户界面(UI)更新或者耗时的操作时,异步编程成为了一个关键的技巧,因为它能够防止应用程序在执行长时间任务时变得无响应,即常说的“冻结”现象。在WinForms应用中,异步操作的实现主要依赖于.NET ...

    C#同步异步操作说明

    在C#编程中,同步和异步操作是两种不同的执行方式,它们主要涉及到程序的执行流程和资源利用效率。同步方法和异步方法的核心区别在于处理任务的方式。 **同步方法**: 同步方法在调用后会阻塞主线程,直到方法执行...

    C#_5.0使用Task_Await_Async实现异步编程

    总之,C# 5.0的 `Task`, `Await` 和 `Async` 提供了一种优雅的方式来编写异步代码,使开发者可以轻松地在不影响用户体验的情况下执行耗时操作。通过理解和运用这些特性,你可以构建更加高效、流畅的应用程序。

    Task C# 异步编程模式,实例分享

    本篇文章将深入探讨C#中的Task异步编程模型,并分享一些实战技巧和实例。 一、Task的基本概念 Task类位于System.Threading.Tasks命名空间中,它是C#异步编程的核心。Task对象表示一个执行中的工作单元,可以是一个...

    c#线程同步与异步编程

    在C#中,我们可以使用`Task`类、`Task.Run`方法或者`BeginXXX/EndXXX`模式来实现异步操作。例如,当需要执行耗时的I/O操作(如文件读写或网络请求)时,可以将这些操作包装成异步方法,避免阻塞主线程,从而提高用户...

    C#实现异步的进度条操作

    在C#编程中,异步进度条操作是一个关键的用户界面(UI)更新技术,它允许在后台执行耗时操作的同时,保持用户界面的响应和更新进度信息。本主题将深入探讨如何使用C#实现这样的功能,特别是涉及`BackgroundWorker`...

    C#实现异步调用外部程序

    在C#编程中,异步调用外部程序是一种常见的需求,尤其在处理耗时操作或者不希望阻塞主线程执行的情况下。本篇文章将深入探讨如何使用C#来实现这一功能,以及相关的知识点。 首先,我们需要了解异步编程的基本概念。...

    C# 同步与异步读写

    此外,C#还提供了`Task.Run`方法,可以在后台线程上执行耗时的操作,以实现类似异步的效果,但这并不等同于真正的异步I/O操作,因为它可能涉及到线程切换,可能带来额外开销。 总之,理解并熟练运用C#中的同步与...

    c#异步task示例分享(异步操作)

    然后,我们使用`Task.Factory.FromAsync`方法来创建一个异步任务,该方法接受两个BeginInvoke和EndInvoke方法的引用,分别用于启动异步操作和完成操作。这里的BeginInvoke和EndInvoke是委托的异步版本,允许我们将...

    c#文件异步操作示例

    本文将深入探讨C#中的文件异步操作,特别是如何使用`FileStream`类进行异步读写。 ### 1. 异步操作的优势 异步操作的主要优点是提高了程序的响应性和用户体验。当进行耗时的I/O操作(如大文件读写)时,程序不会...

    C# 多线程异步执行

    异步编程允许代码在不阻塞主线程的情况下执行操作,这样主线程可以继续处理其他任务,提高用户体验。C#的异步编程基于`async/await`关键字,它们使得编写异步代码变得简洁而直观。 委托事件是C#中实现事件驱动编程...

    c#Winform异步线程刷新UI

    BackgroundWorker组件提供了一个事件驱动的模型,可以在后台执行操作,并在完成时通知主线程。以下是如何使用BackgroundWorker的步骤: 1. **添加BackgroundWorker控件**:在设计视图中,从工具箱中拖动一个...

    C#利用Task实现任务超时多任务一起执行的方法

    Task的异步功能是指Task可以异步地执行某些操作,而不需要等待某些操作完成。这种功能使得Task非常适合于执行一些需要耗时的操作。 4. 实现任务超时 在本文中,我们使用Task.Delay方法来实现任务超时。当某个Task...

Global site tag (gtag.js) - Google Analytics