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