C#之使用任务并行库


.NET Framework4.0引入了一个新的关于异步操作的API,它叫做任务并行库(Task Parallel Library,简称TPL)。TPL的核心是任务,一个任务代表一个异步操作,该操作可以通过多种方式运行,可以使用或不使用独立线程运行。

一个任务可以它通过多种方式与其他方式组合起来,TPL与之前的模式相比,其中一个关键优势就在于其具有用于组合任务的便利的API。

处理任务中的异常结果有多种方式。由于一个任务可能由其他任务组成,这些任务也可能拥有各自的子任务,所以有一个AggregateException的概念。这种异常可以捕获底层任务内部的所有异常,并允许单独处理这些异常。

创建任务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace ThreadDemo
{
    class Proram8
    {
        static void TaskMethod(string name)
        {
            Console.WriteLine($"Task {name} is running on a thread id" +
                $"{Thread.CurrentThread.ManagedThreadId}. Is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}");
        }
        static void Main()
        {
            var t1 = new Task(() => TaskMethod("Task 1"));
            var t2 = new Task(() => TaskMethod("Task 2"));
            t2.Start();
            t1.Start();
            Task.Run(() => TaskMethod("Task 3"));
            Task.Factory.StartNew(() => TaskMethod("Task4"));
            Task.Factory.StartNew(() => TaskMethod("Task5"),TaskCreationOptions.LongRunning);
            Thread.Sleep(TimeSpan.FromSeconds(1));
            
        }
    }
}

output:

Task Task 3 is running on a thread id5. Is thread pool thread:True
Task Task 2 is running on a thread id3. Is thread pool thread:True
Task Task4 is running on a thread id7. Is thread pool thread:True
Task Task5 is running on a thread id8. Is thread pool thread:False
Task Task 1 is running on a thread id4. Is thread pool thread:True

创建任务主要有两种方式,(1)用其构造函数,然后调用Start()方法(2)用Task的静态方法:Run(),Factory.StartNew(),静态方法会立即开始工作,而Run()只是Factory.StartNew()的一个快捷方式,后者有附加选项,如果我们对此附加选项标记为长时间运行,则该任务将不会使用线程池

使用任务执行基本的操作

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program9
    {
        static int TaskMethod(string name)
        {
            Console.WriteLine($"Task {name} is running on a thread id" +
                $"{Thread.CurrentThread.ManagedThreadId}. Is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            return 42;
        }
        static Task CreateTask(string name)
        {
            return new Task(() => TaskMethod(name));
        }
        static void Main()
        {
            TaskMethod("Main Thread Task");//并没有封装到任务中,就是主线程普通的函数
            var task = CreateTask("Task 1");
            task.Start();
            var result = task.Result;//该行为会阻塞主线程
            Console.WriteLine($"Result:{result}");

            task = CreateTask("Task 2");
            task.RunSynchronously();//该任务会运行在主线程上,可以避免使用线程池来执行非常短暂的操作
            result = task.Result;
            Console.WriteLine($"Resultt:{result}");

            task = CreateTask("Task 3");
            Console.WriteLine(task.Status);//打印Start之前的状态
            task.Start();
            while (task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);
            result = task.Result;//阻塞主线程
            Console.WriteLine($"Result is {result}");

        }
    }
}

output:

Task Main Thread Task is running on a thread id1. Is thread pool thread:False
Task Task 1 is running on a thread id3. Is thread pool thread:True
Result:42
Task Task 2 is running on a thread id1. Is thread pool thread:False
Resultt:42
Created
Task Task 3 is running on a thread id4. Is thread pool thread:True
Running
Result is 42

组合任务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program10
    {
        static int TaskMethod(string name,int seconds)
        {
            Console.WriteLine($"Task {name} is running on a thread id" +
                $"{Thread.CurrentThread.ManagedThreadId}.Is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}");
            Thread.Sleep(TimeSpan.FromSeconds(seconds));
            return 42 * seconds;
        }
        static void Main()
        {
            var firstTask = new Task(() => TaskMethod("First Task", 3));
            var secondTask = new Task(() => TaskMethod("Second Task", 2));
            firstTask.ContinueWith(
                t => Console.WriteLine($"The first answer is{t.Result}" +
                $"{Thread.CurrentThread.ManagedThreadId},is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}"),
                TaskContinuationOptions.OnlyOnRanToCompletion
                ) ;
            firstTask.Start();
            secondTask.Start();
            Thread.Sleep(TimeSpan.FromSeconds(4));

            Task continuation = secondTask.ContinueWith(t=>Console.WriteLine($"The second answer is" +
                $"{t.Result}. Thread id" +
                $"{Thread.CurrentThread.ManagedThreadId},is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}"),
                TaskContinuationOptions.OnlyOnRanToCompletion|TaskContinuationOptions.ExecuteSynchronously
                );
            continuation.GetAwaiter().OnCompleted(()=>Console.WriteLine($"" +
                $"Continuation Task completed! Thread id" +
                $"{Thread.CurrentThread.ManagedThreadId},is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}"));
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Console.WriteLine();
            firstTask = new Task(()=>
            {
                var innerTask = Task.Factory.StartNew(()=>TaskMethod("Second Task",5),TaskCreationOptions.AttachedToParent);
                innerTask.ContinueWith(t => TaskMethod("Third Task", 2), TaskContinuationOptions.AttachedToParent);
                return TaskMethod("First Task", 2);
            });
            firstTask.Start();
            while (!firstTask.IsCompleted)
            {
                Console.WriteLine(firstTask.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(firstTask.Status);
            Thread.Sleep(TimeSpan.FromSeconds(10));
        }
    }
}

output:

Task First Task is running on a thread id3.Is thread pool thread:True
Task Second Task is running on a thread id4.Is thread pool thread:True
The first answer is1263,is thread pool thread:True
The second answer is84. Thread id1,is thread pool thread:False
Continuation Task completed! Thread id4,is thread pool thread:True

Running
Task Second Task is running on a thread id5.Is thread pool thread:True
Task First Task is running on a thread id6.Is thread pool thread:True
Running
Running
Running
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
Task Third Task is running on a thread id4.Is thread pool thread:True
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
WaitingForChildrenToComplete
RanToCompletion

将APM模式转换为任务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
    class Program11
    {
        delegate string AsynchronousTask(string threadName);
        delegate string IncompatibleAsynchronousTask(out int threadId);
        static void Callback(IAsyncResult ar)
        {
            Console.WriteLine("Startting a callback...");
            Console.WriteLine($"State passed to a callback:{ar.AsyncState}");
            Console.WriteLine($"Is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}");
            Console.WriteLine($"Thread pool worker thread id:{Thread.CurrentThread.ManagedThreadId}");
        }
        static string Test(string threadName)
        {
            Console.WriteLine("Starting ....");
            Console.WriteLine($"Is thread pool thread:" +
                $"{Thread.CurrentThread.IsThreadPoolThread}");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            Thread.CurrentThread.Name = threadName;
            return $"Thread name:{Thread.CurrentThread.Name}";
        }
        static string Test(out int threadId)
        {
            Console.WriteLine("Startting...");
            Console.WriteLine($"Is thread poool :{Thread.CurrentThread.IsThreadPoolThread}");
            Thread.Sleep(TimeSpan.FromSeconds(2));
            threadId = Thread.CurrentThread.ManagedThreadId;
            return $"Thread pool worker thread id:{threadId}";
        }
        public static void Main()
        {
            int threadId;
            AsynchronousTask d = Test;
            IncompatibleAsynchronousTask e = Test;

            Console.WriteLine("Option 1:");
            Task task = Task.Factory.FromAsync(
                d.BeginInvoke("AsyncTaskThread", Callback, "a delegate asynchronous call"), d.EndInvoke);
            task.ContinueWith(t => Console.WriteLine($"" +
                $"Callback is finished,now running a continuation!Result:{t.Result}"));
            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.WriteLine("----------------------------------------------------");
            Console.WriteLine();
            Console.WriteLine("Option 2:");
            task = Task.Factory.FromAsync(d.BeginInvoke, d.EndInvoke, "AsyncTaskThread", "a delegate asynchronous call");
            task.ContinueWith(t => Console.WriteLine($"Task is completed,now running a continuation!Resultt:{t.Result}"));
            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);
            Thread.Sleep(TimeSpan.FromSeconds(1));
            Console.WriteLine("-------------------------------------------------------");
            Console.WriteLine();
            Console.WriteLine("Option 3:");
            IAsyncResult ar = e.BeginInvoke(out threadId, Callback, "a delegate asynchronous call");
            task = Task.Factory.FromAsync(ar, _ => e.EndInvoke(out threadId, ar));
            task.ContinueWith(t =>
            Console.WriteLine($"Task is completed,now running a continuation!" +
            $"Result:{t.Result},threadId:{threadId}"));
            while (!task.IsCompleted)
            {
                Console.WriteLine(task.Status);
                Thread.Sleep(TimeSpan.FromSeconds(0.5));
            }
            Console.WriteLine(task.Status);
            Thread.Sleep(TimeSpan.FromSeconds(1));
        }
    }
}

output:

Option 1:
Starting ....
Is thread pool thread:True
WaitingForActivation
WaitingForActivation
WaitingForActivation
WaitingForActivation
Startting a callback...
State passed to a callback:a delegate asynchronous call
Is thread pool thread:True
Thread pool worker thread id:3
Callback is finished,now running a continuation!Result:Thread name:AsyncTaskThread
RanToCompletion
----------------------------------------------------

Option 2:
WaitingForActivation
Starting ....
Is thread pool thread:True
WaitingForActivation
WaitingForActivation
WaitingForActivation
Task is completed,now running a continuation!Resultt:Thread name:AsyncTaskThread
RanToCompletion
-------------------------------------------------------

Option 3:
WaitingForActivation
Startting...
Is thread poool :True
WaitingForActivation
WaitingForActivation
WaitingForActivation
Startting a callback...
State passed to a callback:a delegate asynchronous call
Is thread pool thread:True
Thread pool worker thread id:3
Task is completed,now running a continuation!Result:Thread pool worker thread id:3,threadId:3
RanToCompletion

未完待续...

C