.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. python基础-函数(函数参数、返回值、执行、传参、作用域、函数名)

    前言 !!!注意:本系列所写的文章全部是学习笔记,来自于观看视频的笔记记录,防止丢失.观看的视频笔记来自于:哔哩哔哩武沛齐老师的视频:2022 Python的web开发(完整版) 入门全套教程,零基础 ...

  2. AI 核心能力与开发框架工程能力的共生关系解析

    一.本质定位:能力层与载体层的互补 1. AI 能力:突破性认知的"大脑" - 定义:AI 的核心能力(如大语言模型的泛化推理.多模态感知)源于算法创新.海量数据与算力突破,其本质 ...

  3. Forest v1.5.13 发布,声明式 HTTP 框架,已超 1.8k star

    Forest介绍 Forest 是一个开源的 Java HTTP 客户端框架,它能够将 HTTP 的所有请求信息(包括 URL.Header 以及 Body 等信息)绑定到您自定义的 Interfac ...

  4. 迅速理解 LCS 最长公共子序列问题

    在算法与数据结构的经典问题中,最长公共子序列(Longest Common Subsequence,简称 LCS)问题占据着重要的地位.给定两个序列,我们需要找到它们最长的公共子序列,而子序列要求保持 ...

  5. 事务及mysql中的隔离级别

    事务,一个或一组sql语句组成的执行单元,是最小的执行单元,要么全执行,要么全不执行.如果单元中某条sql语句执行失败,整个单元将会回滚,所有受影响的数据返回到事务开始前的状态. 事务具有ACID四个 ...

  6. Apache Tomcat RCE漏洞复现(CVE-2025-24813)

    漏洞描述 该漏洞的核心在于不完整PUT请求上传时 Tomcat 会使用了一个基于用户提供的文件名和路径生成的临时文件,且路径分隔符被替换为 . . 若同时满足以下条件,攻击者可执行任意代码: 默认 S ...

  7. 工作面试必备:SQL 中的各种连接 JOIN 的区别总结!

    前言 尽管大多数开发者在日常工作中经常用到Join操作,如Inner Join.Left Join.Right Join等,但在面对特定查询需求时,选择哪种Join类型以及如何使用On和Where子句 ...

  8. 130道基础OJ编程题之: 89~107

    130道基础OJ编程题之: 89~107 @ 目录 130道基础OJ编程题之: 89~107 89. BC101 班级成绩输入输出 99. BC102 矩阵元素定位 100. BC103 序列重组矩阵 ...

  9. Oralcle11.2.0.1.0使用出现的问题

    问题1:oracle中监听程序当前无法识别连接描述符中请求服务 解决方法1: 查看oracle的服务是否开启,计算机->管理->服务和应用程序->服务,如下图 解决方法2: 找到or ...

  10. SLAM与AI的强强联合

    SLAM与AI的强强联合 本文默认大家已经熟知SLAM.导航.常见AI算法(比如深度学习.强化学习.卷积神经网络.监督学习)等基本概念,不熟的小伙伴可以从我已经出版的书籍<机器人SLAM导航核心 ...