NETCORE-TASK多线程的使用
- 格式:pdf
- 大小:120.65 KB
- 文档页数:4
NETCORE-TASK多线程的使⽤
Task是从 .NET Framework 4 开始引⼊的⼀项基于队列的异步任务(TAP)模式,从 .NET Framework 4.5 开始,任何使⽤ async/await 进
⾏修饰的⽅法,都会被认为是⼀个异步⽅法;实际上,这些异步⽅法都是基于队列的线程任务,从你开始使⽤ Task 去运⾏⼀段代码的时
候,实际上就相当于开启了⼀个线程,默认情况下,这个线程数由线程池 ThreadPool 进⾏管理的。
1. Task 的使⽤⽅法
/// /// 最简单的使⽤⽅式
///
///
[HttpGet]
[Route("GetTask")]
public IActionResult GetTask()
{
Console.ForegroundColor = ConsoleColor.Red;
// 执⾏⼀个⽆返回值的任务
Task.Run(() => { Console.WriteLine("runing ..."); });
// 执⾏⼀个返回 int 类型结果的任务
var res1 = Task.Run(() => { return 483; });
// 声明⼀个任务,仅声明,不执⾏
Task t = new Task(() => { Console.WriteLine("声明"); });
Console.ResetColor();
return Ok("test");
}
2. 使⽤ TaskFactory ⼯⼚开始异步任务
///
/// 使⽤ TaskFactory ⼯⼚开始异步任务
///
///
[HttpGet]
[Route("GetTask2")]
public IActionResult GetTask2()
{
Console.ForegroundColor = ConsoleColor.Red;
List> tasks = new List>();
TaskFactory factory = new TaskFactory();
tasks.Add(factory.StartNew(() => { Console.WriteLine("t1"); return 1; }));
tasks.Add(factory.StartNew(() => { Console.WriteLine("t2"); return 2; }));
tasks.Add(factory.StartNew(() => { Console.WriteLine("t3"); return 3; }));
tasks.ForEach(t => Console.WriteLine("Task:{0}", t.Result));
Console.ResetColor();
return Ok("test2");
}
上⾯的代码使⽤ TaskFactory 创建并运⾏了两个异步任务,同时把这两个任务加⼊了任务列表 tasks 中,然后⽴即迭代此 tasks 获取异步任
务的执⾏结果,使⽤ TaskFactory ⼯⼚类,可以创建⼀组⼈物,然后依次执⾏它们
3. 处理 Task 中的异常
///
/// 处理 Task 中的异常
///
///
[HttpGet]
[Route("GetTask3")]
public IActionResult GetTask3()
{
Console.ForegroundColor = ConsoleColor.Red;
var task = Task.Run(() =>
{
Console.WriteLine("SimpleTask");
Task.Delay(1000).Wait();
throw new Exception("SimpleTask Error");
});
try
{
task.Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (task.IsCompletedSuccessfully)//任务成功
{
Console.WriteLine("IsCompletedSuccessfully");
}
if (task.IsCompleted)//任务完成
{
Console.WriteLine("IsCompleted");
}
Console.ResetColor();
return Ok("test2");
}
异步任务中发⽣异常会导致任务抛出 TaskCancelException 的异常,仅表⽰任务退出,程序应当捕获该异常;然后,⽴即调⽤ Task 进⾏状
态判断,获取内部异常
上⾯的代码模拟了 Task 内部发⽣的异常,并捕获了异常,通常情况下,推荐使⽤ Task 的任务状态判断以进⾏下⼀步的任务处理(如果需
要),如果仅仅是简单的执⾏⼀个异步任务,直接捕获异常即可,这⾥使⽤了状态判断,如果任务已完成,则打印⼀则消息:
IsCompleted;很明显,在上⾯的代码中,此 “IsCompleted” 消息并不会被打印到控制台
注意,这⾥使⽤了 task.IsCompletedSuccessfully ⽽不是 task.IsCompleted,这两者的区别在于,前者只有在任务正常执⾏完成,⽆异常,
⽆中途退出指令的情况下才会表⽰已完成,⽽ task.IsCompleted 则仅仅表⽰“任务完成”
4. 同步上下⽂
在 WinForm/WPF 应⽤程序中,也常常需要在 UI 上开辟异步任务,通常情况下,窗体控件仅允许创建其的线程访问,在没有 Task 的时
代,处理异步上下⽂到同步上下⽂是⼀件⾮常复杂的事情,在 Task 出现以后,提供了 TaskScheduler 任务调度器,让我们可以⾮常⽅便的
在异步线程中访问 UI 线程的资源
///
/// 获取当前线程上下⽂对象
///
///
[HttpGet]
[Route("GetTask4")]
public IActionResult GetTask4()
{
Console.ForegroundColor = ConsoleColor.Red;
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();
var t1 = Task.Factory.StartNew(() => { return 1; }); t1.ContinueWith((atnt) => { Console.WriteLine("从这⾥访问 UI 线程的资源"); }, UISyncContext);
Console.ResetColor();
return Ok("test2");
}
从上⾯的代码可以发现,仅仅需要调⽤ TaskScheduler.FromCurrentSynchronizationContext() 获得当前线程的同步上下⽂,然后在执⾏异
步任务的时候传⼊,即可访问当前线程创建的 UI 资源
5. Task 的运⾏⽅式
基于 ThreadPool 线程池的⽅式
///
///基于 ThreadPool 线程池的⽅式
///
///
[HttpGet]
[Route("GetTask5")]
public IActionResult GetTask5()
{
Console.ForegroundColor = ConsoleColor.Red;
var available = ThreadPool.SetMaxThreads(8, 16);
Console.WriteLine("result:{0}", available);
Console.ResetColor();
return Ok("test2");
}
⼀个异步任务总是处于队列中,任务队列基于先进先出的原则,最先进⼊队列的任务总是最先被执⾏;但是,在多线程的环境下,最先执⾏
并不意味着最先结束,意识到这⼀点很重要,每个任务可调度的资源和处理的进度决定了任务的完成时间。
默认情况下,所有的任务都使⽤ ThreadPool 的资源,当你开启⼀个 Task 的时候,实际上,是由 ThreadPool 分配了⼀个线
程,ThreadPool 的上限取决于很多⽅⾯的因素,例如虚拟内存的⼤⼩,当 Task 开启的数量超过ThreadPool 的上限的时候,Task 将进⼊排
队状态,可以⼿动设置 ThreadPool 的⼤⼩
上⾯的代码表⽰设置当前程序可使⽤的线程池⼤⼩,但是,SetMaxThreads 的值不应该⼩于托管服务器的 CPU 核⼼数量,否则,变量
available 的值将显⽰为 false,表⽰未成功设置线程池上限
注意:ThreadPool 上的所有线程都是后台线程,也就是说,其IsBackground属性是true,在托管程序退出后,ThreadPool 也将会退出。
6. 长时间运⾏于后台的任务
///
/// 长时间运⾏于后台的任务
///
///
[HttpGet]
[Route("GetTask6")]
public IActionResult GetTask6()
{
Console.ForegroundColor = ConsoleColor.Red;
Task.Factory.StartNew(() => { Console.WriteLine("LongRunging Task"); }, TaskCreationOptions.LongRunning);
Console.ResetColor();
return Ok("test2");
}
在创建 Task 的时候,我们可能需要做⼀些长时间运⾏的业务,这个时候如果使⽤默认的 ThreadPool 资源,在并发状态下,这是不合适
的,因为该任务总是长时间的占⽤线程池中的资源,导致线程池数量受限,这种情况下,可以在创建任务的时候使⽤指定
TaskCreationOptions.LongRunning ⽅式创建 Task
7.有条件的 Task