当前位置: 主页 > JAVA语言

java多线程异步并行-java线程池设置线程超时时间

发布时间:2023-02-09 16:20   浏览次数:次   作者:佚名

同步异步编程

同步编程是针对单线程的,就像我们写的控制台程序一样,以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("传参");
        }

java多线程异步并行_ios串行并行同步异步_java线程池设置线程超时时间

private static void Task1() { Thread.Sleep(1000);//模拟耗时操作,睡眠1s Console.WriteLine("前台线程被调用!"); } private static void Task2(object data) { Thread.Sleep(2000);//模拟耗时操作,睡眠2s Console.WriteLine("后台线程被调用!" + data); }

ios串行并行同步异步_java线程池设置线程超时时间_java多线程异步并行

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("我是另一个线程");
}

java线程池设置线程超时时间_ios串行并行同步异步_java多线程异步并行

创建 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的异步编程模型。

任务.结果

java线程池设置线程超时时间_ios串行并行同步异步_java多线程异步并行

.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/

ios串行并行同步异步_java多线程异步并行_java线程池设置线程超时时间

[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); } }

ios串行并行同步异步_java线程池设置线程超时时间_java多线程异步并行

12.PNG

异步和等待

C# async 关键字用于指定自动异步调用方法、Lambda 表达式或匿名方法。

async/await是用来做异步调用的,内部线程池其实是用来管理的。

如果使用await,调用await taskcall()时get main方法线程不会被阻塞,main方法线程会被释放。 新线程执行任务后,继续执行await之后的代码,减少线程切换开销,而之前的线程是空闲的。 向上。

[Route("api/[controller]")]
    public class ValuesController : Controller
    {
        // GET: api/

java线程池设置线程超时时间_java多线程异步并行_ios串行并行同步异步

[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); } }

ios串行并行同步异步_java线程池设置线程超时时间_java多线程异步并行

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线程继续执行。 也就是说,我们减少了那部分等待时间,充分利用了线程。

*** 尽量不要把同步和异步的代码混在一起,如果要异步,异步到底,否则应用可能会因为异常捕获、死锁等原因崩溃。