java多线程异步并行-java线程池设置线程超时时间
同步异步编程
同步编程是针对单线程的,就像我们写的控制台程序一样,以main方法为入口,顺序执行我们写的代码。
异步编程是针对多线程的,通过创建不同的线程来实现多个任务的并行执行。
线
.Net 1.0发布了System.Threading,提供了很多可以显示的类型(如Thread、ThreadStart等)来创建线程。
主线程
每个 Windows 进程只包含一个作为程序入口点的主线程。 进程入口点创建的第一个线程称为主线程。 .Net 执行器(Console、Windows Form、Wpf 等)使用 Main() 方法作为程序入口点。 调用此方法时,将创建主线程。
工作线程
主线程创建的线程可以称为工作线程,用于执行特定的任务。
前台线程
默认情况下,使用 Thread.Start() 方法创建的线程都是前台线程。 前台线程可以防止应用程序终止。 只有当所有前台线程都执行完毕后,CLR 才能关闭应用程序(即卸载托管应用程序域)。 前台线程也是工作线程。
后台线程
后台线程不会影响应用程序的终止。 当所有的前台线程执行完毕后,无论是否执行完毕,后台线程都会被终止。 一般后台线程都是用来做一些无关紧要的任务(比如时不时查邮件,天气app时常更新天气)。 后台线程也是工作线程。
//主线程入口
static void Main(string[] args)
{
Console.WriteLine("主线程开始!");
//创建前台工作线程
Thread t1 = new Thread(Task1);
t1.Start();
//创建后台工作线程
Thread t2 = new Thread(new ParameterizedThreadStart(Task2));
t2.IsBackground = true;//设置为后台线程
t2.Start("传参");
}
private static void Task1()
{
Thread.Sleep(1000);//模拟耗时操作,睡眠1s
Console.WriteLine("前台线程被调用!");
}
private static void Task2(object data)
{
Thread.Sleep(2000);//模拟耗时操作,睡眠2s
Console.WriteLine("后台线程被调用!" + data);
}
111.PNG
执行发现,不会显示【后台线程被调用】。 因为当所有的前台线程都执行完后,应用程序就关闭了,不会等待所有的后台线程执行完,所以不会显示。
创建线程
static void Main(){
new Thread(Go).Start(); // .NET 1.0开始就有的
Task.Factory.StartNew(Go); // .NET 4.0 引入了 TPL
Task.Run(new Action(Go)); // .NET 4.5 新增了一个Run的方法
}
public static void Go(){
Console.WriteLine("我是另一个线程");
}
创建 Thread 的实例后,需要手动调用它的 Start 方法来启动它。 但是对于Task来说,在StartNew和Run的同时,会创建一个新的线程,并且会立即启动。
线程池是为突然出现大量线程而设计的。 它通过有限数量的固定线程来服务于大量的操作,减少创建和销毁线程所需的时间,从而提高效率。 这也是线程池的主要好处。
ThreadPool适用于多个任务并发运行且运行时间不长且互不干扰的场景。
还有一点需要注意的是,通过线程池创建的任务是后台任务。
线程的创建是一个资源密集型的事情。 .NET 为我们提供了线程池来帮助我们创建和管理线程。 Task默认会直接使用线程池,而Thread不会。 如果我们不使用Task而想使用线程池,可以使用ThreadPool类。
static void Main() {
Console.WriteLine("我是主线程:Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
ThreadPool.QueueUserWorkItem(Go);
Console.ReadLine();
}
public static void Go(object data) {
Console.WriteLine("我是另一个线程:Thread Id {0}",Thread.CurrentThread.ManagedThreadId);
}
返回值
任务可以有一个返回值。
static void Main() {
// GetDayOfThisWeek 运行在另外一个线程中
var dayName = Task.Run(() => { return GetDayOfThisWeek(); });
Console.WriteLine("今天是:{0}",dayName.Result);
}
.NET Framework 中已经存在并行任务(Task)和基于任务的异步编程(asynchronously),在.NET Core 平台下也实现了同样的功能。 下面通过.NET Core WebAPI介绍使用Task.result的同步编程和使用await的异步编程模型。
任务.结果
.Net 4.0引入了System.Threading.Tasks,简化了我们进行异步编程的方式,无需直接与线程和线程池打交道。
System.Threading.Tasks 中的类型称为任务并行库 (TPL)。 TPL 使用 CLR 线程池(表示使用 TPL 创建的线程是后台线程)自动动态地将应用程序的工作分配给可用的 CPU。
Result方法可以返回Task执行的结果,如下代码所示:
[HttpGet]
public static async Task GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
public class MyController : ApiController
{
public string Get()
{
var jsonTask = GetJsonAsync(...);
return jsonTask.Result.ToString();
}
}
但是,如果在ASP.NET Core的webapi中使用result方法获取任务输出值,会导致当前API线程阻塞,等待任务执行完成后再继续。 通过下面的代码可以证明java多线程异步并行,get方法有一个线程,调用一个新的线程来执行任务(taskcaller)。 执行任务时,需要等待任务的执行结果。 此时get方法的执行线程正在等待,直到结果输出。 此线程继续完成该方法。
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/
[HttpGet("get")]
public async Task Get()
{
var info = string.Format("api执行线程:{0}", Thread.CurrentThread.ManagedThreadId);
var infoTask = TaskCaller().Result;//使用Result
var infoTaskFinished = string.Format("api执行线程(task调用完成后):{0}", Thread.CurrentThread.ManagedThreadId);
return string.Format("{0},{1},{2}", info, infoTask, infoTaskFinished);
}
private async Task TaskCaller()
{
await Task.Delay(5000);
return string.Format("task 执行线程:{0}", Thread.CurrentThread.ManagedThreadId);
}
}
12.PNG
异步和等待
C# async 关键字用于指定自动异步调用方法、Lambda 表达式或匿名方法。
async/await是用来做异步调用的,内部线程池其实是用来管理的。
如果使用await,调用await taskcall()时get main方法线程不会被阻塞,main方法线程会被释放。 新线程执行任务后,继续执行await之后的代码,减少线程切换开销,而之前的线程是空闲的。 向上。
[Route("api/[controller]")]
public class ValuesController : Controller
{
// GET: api/
[HttpGet("get")]
public async Task Get()
{
var info = string.Format("api执行线程:{0}", Thread.CurrentThread.ManagedThreadId);
var infoTask = await TaskCaller();//使用await
var infoTaskFinished = string.Format("api执行线程(task调用完成后):{0}", Thread.CurrentThread.ManagedThreadId);
return string.Format("{0},{1},{2}", info, infoTask, infoTaskFinished);
}
private async Task TaskCaller()
{
await Task.Delay(5000);
return string.Format("task 执行线程:{0}", Thread.CurrentThread.ManagedThreadId);
}
}
11.PNG
如上截图所示,一开始在10号线程运行,然后跳转到8号线程执行async方法,在没有使用await的情况下,主线程并没有停止,依然按照自己的路径运行,直到async 使用 await 方法。 ,下面的代码也交给了子线程。
至于为什么交给子线程,有一篇文章说,await前后的代码分成block,把await的任务交给线程池。 线程池执行完后java多线程异步并行,执行moveNext方法继续执行await之后的代码。
你可以阅读这篇文章
Task.result 与 await 关键字有类似的作用,用于获取任务的返回值,但本质上 Task.result 会阻塞外层函数的执行线程,直到任务执行完成,而外层函数线程使用 await 关键字不会Block,而是通过任务执行线程执行await之后的代码
异步和等待的优点
以ASP.NET MVC为例,如果不使用async Action,则在Woker线程中执行。 当我们访问一些web服务或者读取文件的时候,Worker线程会被阻塞。
使用async/await可以在我们访问web服务时,将当前的工作线程放开,放回线程池中,让它可以处理其他的请求。 当web服务返回结果给我们时,会随机从线程池中取出一个新的woker线程继续执行。 也就是说,我们减少了那部分等待时间,充分利用了线程。
*** 尽量不要把同步和异步的代码混在一起,如果要异步,异步到底,否则应用可能会因为异常捕获、死锁等原因崩溃。