.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<int> CreateTask(string name)
{
return new Task<int>(() => 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<int>(() => TaskMethod("First Task", 3));
var secondTask = new Task<int>(() => 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<int>(()=>
{
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模式转换为任务

先简单复习下APM的基本情况:

APM,即异步编程模式,这是第一代异步编程的范式,它主要是依靠委托来实现的。

当委托对象在调用列表中只有一个方法(后称为引用方法),它就可以异步执行这个方法。委托类有两个方法,叫做BeginInvoke,EndInvoke,而APM的whole story如下:

  • 当我们调用BeginInvoke时,它开始在一个独立线程上执行引用方法,并且立即返回到原始线程,返回IAsyncResult类,原始线程可以继续,而引用方法会在线程池的线程中并行执行
  • 当程序希望获取已经完成的异步结果时,包括检查引用方法在线程池的执行状态时,可以以检查BeginInvoke返回的IAsyncResult类的IsCompleted属性,或者调用EndInvoke方法来等待委托完成。
  • EndInvoke方法接受一个由BeginInvoke方法返回的IAsyncResult对象的引用,并且释放线程使用的资源,所以必须确保对每一个BeginInvoke都有一个对应的EndIInvoke

由此也衍生了三种标准模式:

  • wait-until-done模式:原始线程发起一个异步方法的调用,做一些处理,然后停止并等待,直到开启的线程结束。这主要是通过在原始线程中,调用var result=delegate.EndInvoke(IAsyncResult)来实现的。Demo如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
delegate long MyDel(int first, int second);//声明委托
class Program12
{
static long sum(int first,int second)
{
Thread.Sleep(100);
Console.WriteLine("This is inside Sum");
return first + second;
}
static void Main()
{
MyDel del = new MyDel(sum);
Console.WriteLine("Before BeginInvoke....");
var iar = del.BeginInvoke(3, 5, null, null);//前两个参数是委托的参数,后两个中,第一个是回调函数,第二个object state
Console.WriteLine("After caling BeginInvoke..");
Console.WriteLine("Doing staff...");
long result = del.EndInvoke(iar);//阻塞原始线程,直到异步引用方法完成
Console.WriteLine("After EndInvoke:{0}", result); }
}
}

output:

Before BeginInvoke....
After caling BeginInvoke..
Doing staff...
This is inside Sum
After EndInvoke:8
  • 轮询模式:原始线程发起了异步方法的调用,做一些处理,然后使用IAsyncResult的IsComplete属性来定期检查开启的线程是否完成,如果完成,原始线程就调用EndInvoke清理,最后继续做其他事情,否则,它做一些其他处理,过一会再检查。Demo如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
delegate long MyDel(int first, int second);//声明委托
class Program12
{
static long sum(int first,int second)
{
Thread.Sleep(100);
Console.WriteLine("This is inside Sum");
return first + second;
}
static void Main()
{
MyDel del = new MyDel(sum);
Console.WriteLine("Before BeginInvoke....");
var iar = del.BeginInvoke(3, 5, null, null);//前两个参数是委托的参数,后两个中,第一个是回调函数,第二个object state
Console.WriteLine("After caling BeginInvoke..");
while (!iar.IsCompleted) //轮询是否完成
{
//如果没完成就做如下操作
Console.WriteLine("Not Done...");
for (long i = 0; i < 1000000; i++)
;
}
//完成
Console.WriteLine("Done!...");
var result = del.EndInvoke(iar);
Console.WriteLine("Result:{0}", result);
}
}
}

output:

Before BeginInvoke....
After caling BeginInvoke..
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
Not Done...
This is inside Sum
Not Done...
Done!...
Result:8

对比以上两个模式的demo,可以发现,轮询模式实际上就是在wait-until-done模式基础让原始线程在异步引用方法操作未完成之前做某些操作,而后者实际缺少了轮询,只能“干巴巴”的等待异步的完成。

  • 回调模式:前两种模式均为原始线程继续它自己的控制流程,直到它直到开启的线程已经完成,然后获取结果,并继续。回调模式的不同之处在于,一旦原始线程发起了异步方法,它就自己管自己了,不再考虑同步。而异步的引用方法调用结束后,系统调用一个用户自定义的方法来处理将结果,并且调用委托的EndInvoke方法,这个用户自定义的 方法就叫做回调方法。Demo如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks; namespace ThreadDemo
{
delegate long MyDel(int first, int second);//声明委托
class Program12
{
static long sum(int first,int second)
{
Thread.Sleep(100);
Console.WriteLine("This is inside Sum");
return first + second;
}
//回调函数
static void CallWhenDone(IAsyncResult iar)
{
Console.WriteLine("Inside CallWhenDone");
AsyncResult ar = (AsyncResult)iar;//将传入的iar强制转换为AsyncResult类
MyDel del = (MyDel)ar.AsyncDelegate;//将AsyncResult类强制转换为委托类,AsyncDelegate
var result = del.EndInvoke(iar);//清理
Console.WriteLine("The result is {0}", result);
}
static void Main()
{
MyDel del = new MyDel(sum);
Console.WriteLine("Before BeginInvoke....");
var iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone), null);//前两个参数是委托的参数,后两个中,第一个是回调函数,第二个object state
Console.WriteLine("Doing more work in Main");
Thread.Sleep(500);
Console.WriteLine("Done with Main,Exiting");
}
}
}

output:

Before BeginInvoke....
Doing more work in Main
This is inside Sum
Inside CallWhenDone
The result is 8
Done with Main,Exiting

下面学习将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<string> task = Task<string>.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<string>.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<string>.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

将APM转换为TPL的关键点是Task<T>.Factory.FromAsync,T是异步操作结果的类型,该方法有数个重载。第一个例子中传入了IAsyncResultFunc<IAsyncResult,string>,实现无缝衔接,将BeginInvoke的返回值,在异步操作结束后,赋值给EndInvoke。除此之外,还组合了任务,体现了TPL的优越性。

第二个例子,使用了不同的FromAsync方法,不允许采用回调函数,但我们可以使用后续操作替代它,但如果回调函数很重要,可以使用第一个例子的方法。

第三个例子,由于Task中不接受out参数,因此必须先单独BeginInvoke,而后将其返回值带入FromAsync方法中,然后将EndInvoke封装到一个lambda表达式中,从而适合任务工厂方法。

将EAP模式转换为任务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.ComponentModel; namespace ThreadDemo
{
class Program13
{
static TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
static int TaskMethod(string name,int seconds)
{
Console.WriteLine($"" +
$"Task {name} is running on a thread id" +
$"{Thread.CurrentThread.ManagedThreadId}.Is thread a pool thread" +
$"{Thread.CurrentThread.IsThreadPoolThread}");
Thread.Sleep(TimeSpan.FromSeconds(seconds));
return 42 * seconds;
}
static void Main()
{ var worker = new BackgroundWorker();
worker.DoWork += dowork; ;//绑定事件
worker.RunWorkerCompleted += Worker_RunWorkerCompleted;
worker.RunWorkerAsync();
var result = tcs.Task.Result;
Console.WriteLine($"Result is {result}");
} private static void Worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
tcs.SetException(e.Error);
}
else if (e.Cancelled)
{
tcs.SetCanceled();
}
else
{
tcs.SetResult((int)e.Result);
} } private static void dowork(object sender, DoWorkEventArgs e)
{
e.Result = TaskMethod("Background worker", 5);
}
}
}

output:

Task Background worker is running on a thread id3.Is thread a pool threadTrue
Result is 210

实现取消

取消机制同样是运用CancellationTokenSourceCancellationToken类进行控制,其中后者是前者的Token属性,前者通过调用Cancel方法,后者的IsCancellationRequested属性就被设置为True。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
class Program14
{
static int TaskMethod(string name,int seconds,CancellationToken token)
{
Console.WriteLine($"" +
$"Task {name} is running on a thread id" +
$"{Thread.CurrentThread.ManagedThreadId}. Is" +
$"Thread pool thread:{Thread.CurrentThread.IsThreadPoolThread}"); for(int i = 0; i < seconds; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(1));
if (token.IsCancellationRequested) return -1;
}
return 42 * seconds;
}
static void Main()
{
var cts = new CancellationTokenSource();
var longTask = new Task<int>(() => TaskMethod("Task 1", 10, cts.Token), cts.Token);
Console.WriteLine(longTask.Status);
cts.Cancel();
Console.WriteLine(longTask.Status);
Console.WriteLine("First task has been cancelled before execution"); cts = new CancellationTokenSource();
longTask = new Task<int>(() => TaskMethod("Task 2", 10, cts.Token), cts.Token);
longTask.Start();
for(int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}
cts.Cancel();
for(int i = 0; i < 5; i++)
{
Thread.Sleep(TimeSpan.FromSeconds(0.5));
Console.WriteLine(longTask.Status);
}
Console.WriteLine($"A task has been completed with result {longTask.Result}");
} }
}

output:

Created
Canceled
First task has been cancelled before execution
Task Task 2 is running on a thread id3. IsThread pool thread:True
Running
Running
Running
Running
Running
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
RanToCompletion
A task has been completed with result -1

仔细看longTask的创建代码,我们给底层任务传递一次取消标志,然后给任务的构造函数再传递一次,这样做的目的是,如果在任务执行前就取消它,那么它根本就不会运行,这得益于构造函数中传入的取消标志,如果取消了,再试图对该任务调用Start方法,就会得到一个InvalidOperationException异常。

这里需要注意的是,当任务运行后,我们再对其进行取消,任务的状态是RanToCompletion,因为从TPL的角度来看,该任务正常完成了它的工作,这与任务根本没有执行就被取消的Cancel状态是不同的。

处理任务中的异常

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
class Program15
{
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));
throw new Exception("Boom...");
return 42 * seconds;
}
static void Main()
{
Task<int> task;
try
{
task = Task.Run(() => TaskMethod("Task 1", 2));
var result = task.Result;
Console.WriteLine($"Result:{result}");
}
catch(Exception e)
{
Console.WriteLine($"Exception caught:{e}");
}
Console.WriteLine("-------------------------------------------------------");
try
{
task = Task.Run(() => TaskMethod("Task 2", 2));
var result = task.GetAwaiter().GetResult();
Console.WriteLine($"Result:{result}");
}
catch(Exception e)
{
Console.WriteLine($"Exception cautht:{e}");
}
Console.WriteLine("-------------------------------------------------------");
var t1 = new Task<int>(() => TaskMethod("Task 3", 3));
var t2 = new Task<int>(() => TaskMethod("Task 4", 2));
var complexTask = Task.WhenAll(t1, t2);
var exceptionHandler = complexTask.ContinueWith(t => Console.WriteLine($"Exception caught:{t.Exception}"), TaskContinuationOptions.OnlyOnFaulted);
t1.Start();
t2.Start();
Thread.Sleep(TimeSpan.FromSeconds(5));
}
}
}

output:

Task Task 1 is running on a thread id3. Is thread pool thread:True
Exception caught:System.AggregateException: 发生一个或多个错误。 ---> System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_3() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 25
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()
--- 内部异常堆栈跟踪的结尾 ---
在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
在 System.Threading.Tasks.Task`1.get_Result()
在 ThreadDemo.Program15.Main() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 26
---> (内部异常 #0) System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_3() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 25
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()<--- -------------------------------------------------------
Task Task 2 is running on a thread id4. Is thread pool thread:True
Exception cautht:System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_4() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 36
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
在 ThreadDemo.Program15.Main() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 37
-------------------------------------------------------
Task Task 4 is running on a thread id4. Is thread pool thread:True
Task Task 3 is running on a thread id3. Is thread pool thread:True
Exception caught:System.AggregateException: 发生一个或多个错误。 ---> System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_0() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 45
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()
--- 内部异常堆栈跟踪的结尾 ---
---> (内部异常 #0) System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_0() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 45
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()<--- ---> (内部异常 #1) System.Exception: Boom...
在 ThreadDemo.Program15.TaskMethod(String name, Int32 seconds) 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 17
在 ThreadDemo.Program15.<>c.<Main>b__1_1() 位置 E:\LearningCSharp\ThreadTestNetFramework\ThreadDemo\Program15.cs:行号 46
在 System.Threading.Tasks.Task`1.InnerInvoke()
在 System.Threading.Tasks.Task.Execute()<---
请按任意键继续. . .

并行运行任务

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ThreadDemo
{
class Program16
{
static int TaskMethod(string name,int seconds)
{
Console.WriteLine($"" +
$"Task {name} is running on a thread id" +
$"{Thread.CurrentThread.ManagedThreadId}. Is tthread pool thread:" +
$"{Thread.CurrentThread.IsThreadPoolThread}");
Thread.Sleep(TimeSpan.FromSeconds(seconds));
return 42 * seconds;
}
static void Main()
{
var firstTask = new Task<int>(() => TaskMethod("Task one", 3));
var secondTask = new Task<int>(() => TaskMethod("Task two", 2));
var whenAllTask = Task.WhenAll(firstTask, secondTask);//创建了第三个任务,该任务将会在所有任务完成后运行,该任务的结果提供了 一个 结果数组。
whenAllTask.ContinueWith(t =>
Console.WriteLine($"The first answer is {t.Result[0]}, the second answer is {t.Result[1]}"), TaskContinuationOptions.OnlyOnRanToCompletion); firstTask.Start();
secondTask.Start();
Thread.Sleep(TimeSpan.FromSeconds(4)); var tasks = new List<Task<int>>();
for(int i = 1; i < 4; i++)
{
int counter = i;
var task = new Task<int>(() => TaskMethod($"Task {counter}", counter));
tasks.Add(task);
task.Start();
}
while (tasks.Count > 0)
{
var completedTask = Task.WhenAny(tasks).Result;
tasks.Remove(completedTask);
Console.WriteLine($"A task has been completed with result {completedTask.Result}");
}
Thread.Sleep(TimeSpan.FromSeconds(1));
}
}
}

output:

Task Task one is running on  a thread id3. Is tthread pool thread:True
Task Task two is running on a thread id4. Is tthread pool thread:True
The first answer is 126, the second answer is 84
Task Task 3 is running on a thread id3. Is tthread pool thread:True
Task Task 1 is running on a thread id5. Is tthread pool thread:True
Task Task 2 is running on a thread id4. Is tthread pool thread:True
A task has been completed with result 42
A task has been completed with result 84
A task has been completed with result 126

Task.WaitAll是创建 了一个新的任务,本例中类型为Task<int[]>,返回的结果是一个int数组,ContinueWith中传入的第一个参数是lambda表达式,该表达式的参数正是任务本身。

Task.WaitAny是传入任务列表中的任何已经完成的一个任务。

需要注意的是在使用for循环,创建Task时候,需要有中间变量int countter=i,如果直接在Task中,使用i,则可能会重复。因为如果是中间变量counter,则迫使传入Task的是每次循环的i.

使用TaskScheduler配置任务的执行

<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<TextBlock x:Name="ContentTextBlock" HorizontalAlignment="Left"
Margin="44,134,0,0" VerticalAlignment="Top" Width="425" Height="40"/>
<Button Content="Sync" HorizontalAlignment="Left" Margin="45,190,0,0" VerticalAlignment="Top" Width="75"
Click="ButtonSync_Click"/>
<Button Content="Async" HorizontalAlignment="Left" Margin="165,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsync_Click"/>
<Button Content="Async OK" HorizontalAlignment="Left" Margin="285,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsyncOk_Click"/>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace WpfApp1
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
} private void ButtonSync_Click(object sender, RoutedEventArgs e)
{
ContentTextBlock.Text = string.Empty;
try
{
string result = TaskMethod().Result;
ContentTextBlock.Text = result;
}
catch(Exception ex)
{
ContentTextBlock.Text = ex.InnerException.Message;
}
} private void ButtonAsync_Click(object sender, RoutedEventArgs e)
{
ContentTextBlock.Text = string.Empty;
Mouse.OverrideCursor = Cursors.Wait;
Task<string> task = TaskMethod();
task.ContinueWith(t =>
{
ContentTextBlock.Text = t.Exception.InnerException.Message;
Mouse.OverrideCursor = null;
},CancellationToken.None,TaskContinuationOptions.OnlyOnFaulted,TaskScheduler.FromCurrentSynchronizationContext());
} private void ButtonAsyncOk_Click(object sender, RoutedEventArgs e)
{
ContentTextBlock.Text = string.Empty;
Mouse.OverrideCursor = Cursors.Wait;
Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext());
task.ContinueWith(t => Mouse.OverrideCursor = null, CancellationToken.None, TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<string> TaskMethod()
{
return TaskMethod(TaskScheduler.Default);
}
Task<string> TaskMethod(TaskScheduler scheduler)
{
Task delay = Task.Delay(TimeSpan.FromSeconds(2));
return delay.ContinueWith(t=>
{
string str = $"Task is running on a thread id " +
$"{Thread.CurrentThread.ManagedThreadId}. Is thread pool thread:" +
$"{Thread.CurrentThread.IsThreadPoolThread}";
ContentTextBlock.Text = str;
return str; },scheduler);
}
}
}

C#之使用任务并行库的更多相关文章

  1. C#并行库(TaskParallelLibrary)用法小结

    今天有空,总结一下.NET 4.5并行库(TaskParallelLibrary)用法. 也许C和C++的程序员刚刚开始写C#还习惯于new Thread来新建一个线程,但新建线程需要内存和CPU上下 ...

  2. C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是TAP(Task-based Asynchronous Pattern, 基于任务的异步模式)

    学习书籍: <C#本质论> 1--C#5.0之后推荐使用TPL(Task Parallel Libray 任务并行库) 和PLINQ(Parallel LINQ, 并行Linq). 其次是 ...

  3. [C#]『PLINQ』任务并行库使用小计

    并行 LINQ (PLINQ) 是 LINQ to Objects 的并行实现. PLINQ 实现完整的 LINQ 标准查询运算符集作为 T:System.Linq 命名空间的扩展方法,并具有用于并行 ...

  4. C#当中的多线程_任务并行库(上)

    复习: 第三章内容中我们提到了三种异步编程模型,这里简单复习一下,分别如下 1.APM(异步编程模式):形如Beginxxx,Endxxx. 2.EAP(基于事件的异步编程模式):这个我们在.net中 ...

  5. Delphi XE7中新并行库

    Delphi XE7中添加了新的并行库,和.NET的Task和Parellel相似度99%. 详细内容能够看以下的文章: http://www.delphifeeds.com/go/s/119574 ...

  6. DELPHI XE7 新的并行库

    DELPHI XE7 的新功能列表里面增加了并行库System.Threading, System.SyncObjs. 为什么要增加新的并行库? 还是为了跨平台.以前要并行编程只能从TThread类继 ...

  7. C#多线程编程系列(五)- 使用任务并行库

    目录 1.1 简介 1.2 创建任务 1.3 使用任务执行基本的操作 1.4 组合任务 1.5 将APM模式转换为任务 1.6 将EAP模式转换为任务 1.7 实现取消选项 1.8 处理任务中的异常 ...

  8. Delphi并行库System.Threading 之ITask 1

    不知什么时候,也许是XE8,也许是XE8之前 .Delphi里面多了个System.Threading的并行库. 虽然己经有非常棒的第三方并行库QWorker,但我还是更喜欢官方的东西. 下面是一段使 ...

  9. Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) JAVA日志的前世今生 .NET MVC采用SignalR更新在线用户数 C#多线程编程系列(五)- 使用任务并行库 C#多线程编程系列(三)- 线程同步 C#多线程编程系列(二)- 线程基础 C#多线程编程系列(一)- 简介

    Python GUI之tkinter窗口视窗教程大集合(看这篇就够了) 一.前言 由于本篇文章较长,所以下面给出内容目录方便跳转阅读,当然也可以用博客页面最右侧的文章目录导航栏进行跳转查阅. 一.前言 ...

  10. C#使用任务并行库(TPL)

    TPL(Task Parallel Library) 任务并行库 (TPL) 是 System.Threading和 System.Threading.Tasks 命名空间中的一组公共类型和 API. ...

随机推荐

  1. 另类方式实现.Net下的多语言

    前言 关于.Net下的多语言方案已经有很多成熟的方案,例如:# Avalonia 国际化之路:Resx 资源文件的深度应用与探索,或者# Avalonia使用XML文件实现国际化,基本都围绕官方的Sa ...

  2. Qt QVariant 与 自定义类型转换的方法

    Example: 1. 声明自定义类型可用于QVariant,类也能用,也是这样,QT的基本数据类型不用声明就可以用,而且存入是什么类型,拿出来还是什么类型 #include <QMetaTyp ...

  3. autMan奥特曼机器人-内置wx机器人的相关说明

    内置wx机器人的相关说明 内置wxbot机器人,经常有人说在群内无回复,做以下几个工作: 给群命名 通过机器人微信APP将此群加入到通讯录 重启autMan 内置微信机器人已经支持群名设置 例如转发时 ...

  4. ServerMmon青蛇探针,一个超好用的服务器状态监控-搭建教程

    serverMmon(青蛇探针)是nodeJs开发的一个酷炫高逼格的云探针.云监控.服务器云监控.多服务器探针~. 在线演示:http://106.126.11.114:5880/ 主要功能: 全球服 ...

  5. 他来了,为大模型量身定制的响应式编程范式(1) —— 从接入 DeepSeek 开始吧

    哒哒哒,他来了! 今天我们要介绍一种新型的 Java 响应式大模型编程范式 -- FEL.你可能听说过 langchain,那么你暂且可以把 FEL 看作是 Java 版本的 langchain. 话 ...

  6. php如何处理表单和数据库字段不匹配

    // 假设从表单接收到的数据 $formData = [ 'first_name' => $_POST['first_name'], 'last_name' => $_POST['last ...

  7. Ctfhub-SSRF部分做题记录

    Ctfhub-SSRF部分做题记录 上传文件 提示:这次需要上传一个文件到flag.php了.祝你好运 进入flag.php 发现没有提交按钮 修改源代码,加个提交按钮 抓包 修改host为127.0 ...

  8. 删除空白行,报错 raise KeyError(list(np.compress(check, subset))) KeyError: ['姓名']

    之前这段程序跑完后没报错,经调试发现到这一句报错 > df.dropna(subset=['姓名'],inplace=True) 意思是删除'姓名'列 含有'NAN'的行 思考第一步:应该是 d ...

  9. protobuf优缺点及编码原理

    什么是protobuf protobuf(Google Protocol Buffers),官方文档对 protobuf 的定义:protocol buffers 是一种语言无关.平台无关.可扩展的序 ...

  10. JAVA调用Python脚本执行

    SpringBoot-web环境 <dependency> <groupId>org.springframework.boot</groupId> <arti ...