您的位置:首页 > 编程学习 > C#

C# Task实现多线程

更多 2015/10/21 来源:C#学习浏览量:3589
学习标签: 多线程
本文导读:C# 中Task跟线程池ThreadPool的功能类似,不过写起来更为简单,直观,代码更简洁了,使用Task来进行操作,可以跟线程一样可以轻松的对执行的方法进行控制。Task使用时需要引用名称空间System.Threading.Tasks,下面为大家C#中利于task实现多线程的方法

一、Task 的优点以及功能

1、在任务启动后,可以随时以任务延续的形式注册回调。

2、通过使用 ContinueWhenAll 和 ContinueWhenAny 方法或者 WaitAll 方法或 WaitAny 方法,协调多个为了响应 Begin_ 方法而执行的操作。

3、在同一Task 对象中封装异步 I/O 绑定和计算绑定操作。

4、监视Task 对象的状态。

5、使用TaskCompletionSource 将操作的状态封送到Task 对象。

 

二、创建 Task

 

1、使用构造函数创建

使用Task的构造函数来创建任务,并调用Start方法来启动任务并执行异步操作。

例如

 

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

         }

 

2、使用Task.Factory.StartNew 进行创建Task

Task.Factory 是对Task进行管理,调度管理这一类的。

例如

 

var task1 = Task.Factory.StartNew(() => DoSomeWork()).ContinueWith(
                  task => { Console.WriteLine(task.Result.ToString()); }).ContinueWith(
                  task => { Console.WriteLine(task.Result.ToString()); });

 

三、等待任务的完成并获取返回值

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

例如

 

static void TaskWait() {
       //创建任务
       Task<int> task = new Task<int>(() =>
       {
           int sum = 0;
           Console.WriteLine("使用Task执行异步操作.");
           for (int i = 0; i < 100; i++)
           {
               sum+=i;
           }
           return sum;
       });
       //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
       task.Start();
 
       Console.WriteLine("主线程执行其他处理");
       //等待任务的完成执行过程。
       task.Wait();
       //获得任务的执行结果
       Console.WriteLine("任务执行结果:{0}", task.Result.ToString());
}

 

四、等待所有线程结束

 

 var task1 = Task.Factory.StartNew(() => DoSomeWork());
 var task2 = Task.Factory.StartNew(() => DoSomeWork());
 var task3 = Task.Factory.StartNew(() => DoSomeWork());
 Task.WaitAll(task1, task2, task3);

 

五、等待其中一个线程结束

 

 
 var task1 = Task.Factory.StartNew(() => DoSomeWork());
 var task2 = Task.Factory.StartNew(() => DoSomeWork());
 var task3 = Task.Factory.StartNew(() => DoSomeWork());
 Task.WaitAny(task1, task2, task3);

 

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

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

 

static void TaskContinueWith()
{
    //创建一个任务
    Task<int> task = new Task<int>(() =>
    {
          int sum = 0;
          Console.WriteLine("使用Task执行异步操作.");
          for (int i = 0; i < 100; i++)
          {
              sum += i;
          }
          return sum;
    });
    //启动任务,并安排到当前任务队列线程中执行任务(System.Threading.Tasks.TaskScheduler)
    task.Start();
    Console.WriteLine("主线程执行其他处理");
    //任务完成时执行处理。
    Task cwt = task.ContinueWith(t => { 
         Console.WriteLine("任务完成后的执行结果:{0}", t.Result.ToString()); 
    });
    Thread.Sleep(1000);
}

 

七、创建一组具有相同状态的任务

可以使用TaskFactory类或TaskFactory<TResult>类。这两个类创建一组任务时可以指定任务的CancellationToken、TaskCreationOptions、TaskContinuationOptions和TaskScheduler默认值。

例如



static void TaskFactoryApply()
{
    Task parent = new Task(() =>
    {
        CancellationTokenSource cts = new CancellationTokenSource(5000);
        //创建任务工厂
        TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
        //添加一组具有相同状态的子任务
        Task[] task = new Task[]{
            tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第一个任务。"); }),
            tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第二个任务。"); }),
            tf.StartNew(() => { Console.WriteLine("我是任务工厂里的第三个任务。"); })
        };
   });
   parent.Start();
   Console.Read();
}

 

八、任务内部实现和任务调度

 

1、任务内部有一组构成任务状态的属性,标识任务的唯一Id、表示任务的执行状态(TaskStatus)、任务创建时提供的回调函数的引用和传递给回调函数的数据对象AsyncState、对任务创建时的任务调度对象(TaskScheduler)的引用、对父任务的引用以及对执行上下文的引用和ManualResetEventSlim对象的引用。

2、Task类和Task<TResult>类都实现了标准的释放资源的接口,允许在任务完成处理的时候使用Dispose方法释放资源(关闭ManualResetEventSlim对象实例)。

3、可以使用Task类的CurrentId属性获得正在执行的任务的Id,如果没有任务在执行CurrentId返回值为null,CurrentId是一个int?可空类型的属性。

4、任务执行的生命周期通过TaskStatus类型的一个值来表示,TaskStatus所包含的值:

 


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

5、在任务内部由TaskScheduler类调度任务的执行,该类是一个抽象类,FCL中从他派生了两个派生类:

ThreadPoolTaskScheduler线程池任务调度器

所有任务默认都是采用ThreadPoolTaskScheduler调度任务,他是采用线程池来执行任务,可以通过TaskScheduler类的静态属性Default获得对默认任务调度器的引用。

SynchronizationContextTaskScheduler同步上下文任务调度器

SynchronizationContextTaskScheduler任务调度器能够用在Window form、WPF等应用程序,他的任务调度是采用的GUI线程,所以他能同步更新UI组件,可以通过TaskScheduler类的静态方法FromCurrentSynchronizationContext获得对一个同步上下文任务调度起的引用。

6、例如

 


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

 

收藏
415
很赞
484
您可能感兴趣