Tokio runtime BlockingPool源码解析

我是蚂蚁背大象(Apache EventMesh PMC&Committer),文章对你有帮助给项目rocketmq-rust star,关注我GitHub:mxsm,文章有不正确的地方请您斧正,创建ISSUE提交PR~谢谢! Emal:mxsm@apache.com

1. 代码结构

BlockingPool的代码结构如下图所示:

接下来我们来分析代码

2. 线程如何创建

Spawner 是创建线程的入口:

Spawner#spawn_blocking_inner 内部方法负责转换将func变量转换成 Task然后进行包装:

然后通过 Spawner#spawn_task 方法来创建线程。 这个方法也是BlockingPool的主要逻辑:

上面的方法主要有两块:

  • 线程池中存在空闲线程,如果存在空闲的线程会从条件变量中等待的线程队列中唤醒线程来处理任务。

  • 线程池中不存在空闲线程,这种情况下分为两种情况:

    1. 线程池达到了最大的线程数量,那么这种情况下Task只需要存入任务队列即可
    2. 线程池的线程没有达到最大数量,那么就是创建线程也就是调用spawn_thread方法来创建

创建线程:

到了这里就已经揭秘了整个线程池中的线程创建过程。分析倒底层发现线程创建还是调用Rust最初的的方法。

3. 任务如何被线程池中的线程执行

线程创建中有这样的一段代码:rt.inner.blocking_spawner().inner.run(id) 接下来分析这段代码,我们可以将这个方法中的代码分为三部分:

  1. 线程执行任务之前
  2. 线程执行任务
  3. 任务队列中没有任务执行,也就是执行任务退出后

3.1 线程执行任务之前

执行线程启动后的Callback回调函数。

3.2 线程执行任务

通过一个loop循环以及线程在条件上面等待的配合来实现线程池中线程的处理任务。

  1. 线处理线程池队列中的任务
  2. 当任务处理完成,那么此时线程池中的空闲线程增加
  3. 线程池在没有shutdown的情况下将此线程挂起来,设置对应的挂起时间。如果此时来了Task会有等待的线程唤醒。
  4. 当没有任务此时就会判断线程池是否shutdown以及超时,此时会重新进入loop
  5. 当线程池shutdown的时候将任务队列中的任务处理,然后退出loop

这个和Java中线程池的实现差不多。整体的实现思路都一样。

线程池中的线程如何保持?

loop的循环让线程一直运行,同时配合线程的等待挂起来防止一直的空轮训。

3.3.执行任务退出后

线程池shutdown后主要是处理后线程池中的一些可观察性的状态以及线程退出的回调函数。

通过上面的分析会发现,当线程池达到最大线程数后不会根据超时时间降下来,也就是没有像Java线程池那样,如果最大线程池的数据和核心线程池的线程数量不一样,当没有任务后一定时间后线程数量会降到和核心线程数量一致的情况。

4. 总结

在 Tokio 中,blockingPool 提供了一种在异步环境中执行阻塞操作的方法,以避免在单个线程上阻塞整个事件循环。以下是 Tokio 中 blockingPool 的执行过程的简要总结:

  1. 创建阻塞任务 : 当需要执行一个阻塞操作时,可以通过 blockingPool 创建一个阻塞任务。这个任务通常是一个异步闭包,其中包含需要执行的阻塞操作。
  2. 提交任务 : 创建的阻塞任务可以通过 blockingPool.spawn() 方法提交给 Tokio 的阻塞池。这个方法会将阻塞任务放入一个专门的线程池中执行,以避免阻塞主事件循环。
  3. 执行阻塞操作: 阻塞任务被提交到阻塞池后,Tokio 会将其调度到一个空闲的工作线程上执行。在这个线程上,阻塞任务可以安全地执行任何需要阻塞等待的操作,如文件 I/O、网络 I/O 或 CPU 密集型计算等。
  4. 非阻塞等待: 在主事件循环中,阻塞任务执行时不会阻塞整个事件循环。相反,Tokio 会在后台运行其他异步任务,并在阻塞任务完成后及时返回结果。
  5. 返回结果: 一旦阻塞任务执行完成并返回结果,Tokio 将会将结果返回给调用方,从而完成整个阻塞操作的过程。

通过使用 blockingPool,可以确保在 Tokio 的异步环境中执行阻塞操作时不会阻塞主事件循环,保持整个应用程序的响应性和性能。

相关推荐
栗豆包37 分钟前
w175基于springboot的图书管理系统的设计与实现
java·spring boot·后端·spring·tomcat
萧若岚2 小时前
Elixir语言的Web开发
开发语言·后端·golang
Channing Lewis2 小时前
flask实现重启后需要重新输入用户名而避免浏览器使用之前已经记录的用户名
后端·python·flask
Channing Lewis2 小时前
如何在 Flask 中实现用户认证?
后端·python·flask
一只爱吃“兔子”的“胡萝卜”3 小时前
2.Spring-AOP
java·后端·spring
AI向前看3 小时前
PHP语言的软件工程
开发语言·后端·golang
湫qiu3 小时前
带你写HTTP/2, 实现HTTP/2的编码
java·后端·http
m0_748239473 小时前
springBoot发布https服务及调用
spring boot·后端·https
Pandaconda3 小时前
【Golang 面试题】每日 3 题(四十一)
开发语言·经验分享·笔记·后端·面试·golang·go
Like_wen3 小时前
【Go面试】基础八股文篇 (持续整合)
java·后端·计算机网络·面试·golang·go·八股文