首页 > 其他分享 >Chromium 消息循环和线程池详解

Chromium 消息循环和线程池详解

时间:2023-10-24 10:45:02浏览次数:34  
标签:task MessageLoop 创建 详解 线程 base include Chromium

Chromium 中的多线程机制由 base 库提供,要理解 Chromium 中的多线程机制,首先要理解的概念就是 base::MessageLoop 和 base::TaskScheduler ,它们两个是 Chromium 多线程的基础

1. MessageLoop详解

base::MessageLoop 代表消息循环,它不会主动创建新的线程,默认情况下它使用当前线程(你也可以手动把它 Bind 到指定的线程上),它只负责消息(任务)循环,它提供了 task_runner() 方法用于获取 TaskRunner 对象,你需要使用 TaskRunner::PostTask*() 方法来向该消息循环分发消息,默认情况下这些消息(任务)会在当前线程运行。因此,你可以在当前线程中创建 MessageLoop 并且在当前线程中向它 Post 消息,并且这些消息(任务)会在当前线程执行。

每一个通过base::Thread创建出来的线程都拥有一个 MessageLoop,但是主线程(main 函数所在的线程)不是通过base::Thread来创建的,它又需要处理消息循环,因此需要手动给主线程创建MessageLoop,这个过程一般在程序的入口处进行。

你可以使用 base::MessageLoopCurrent::Get() 静态方法获取当前线程的MessageLoop对象,从而使用 base::MessageLoopCurrent::Get()->task_runner()→PostTask*() 方法来创建任务。

一旦你创建了一个 MessageLoop 对象,它会自动 Bind 到当前线程(通过线程依赖的 ThreadLocal 机制来实现)。

 比较常规的使用方式可以参考下面:

 1 #include "base/logging.h"
 2 #include "base/message_loop/message_loop.h"
 3 #include "base/message_loop/message_loop_current.h"
 4 #include "base/task/post_task.h"
 5 #include "base/task/single_thread_task_executor.h"
 6 #include "base/task/thread_pool/thread_pool_impl.h"
 7 #include "base/task/thread_pool/thread_pool_instance.h"
 8 #include "base/threading/thread_task_runner_handle.h"
 9 #include "base/timer/timer.h"
10 
11 void Hello() {
12   LOG(INFO) << "hello,demo!";
13 }
14 
15 int main(int argc, char** argv) {
16   // 创建消息循环
17   base::MessageLoop message_loop;
18   // 也可以使用下面的方法。它们的区别仅在于 MessageLoop 对外暴露了更多的内部接口。
19   // 在当前线程创建一个可执行 task 的环境,同样需要使用 RunLoop 启动
20   // base::SingleThreadTaskExecutor main_task_executer;
21 
22   base::RunLoop run_loop;
23 
24   // 使用 message_loop 对象直接创建任务
25   message_loop.task_runner()->PostTask(FROM_HERE, base::BindOnce(&Hello));
26   // 获取当前线程的 task runner
27   base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
28                                                 base::BindOnce(&Hello));
29 
30   // 启动消息循环,即使没有任务也会阻塞程序运行。当前进程中只有一个线程。
31   run_loop.Run();
32 
33   return 0;
34 }

 

2. MessageLoop 的运行流程

 

3. MessageLoop 的类图

 类图中已经介绍了主要类的功能,这里不再赘述,简单总接一下就是:MessageLoop 创建消息/任务循环,并且绑定到当前线程,RunLoop 启动消息循环,调用者通过 TaskRunner 来创建任务。

 

4. TaskScheduler详解

base::TaskScheduler 直译为任务调度器,也可以叫做线程池,你需要使用它的 static 类型的 Create*() 相关方法来构造它,使用 Start() 方法来启动它,或者通过 CreateAndStartWithDefaultParams() 方法来同时创建并启动线程池。默认情况下他会创建 3 个线程,1 个 Service 线程,2 个 Worker 线程。Service 线程只用来调度延时任务,Worker 线程用来执行任务。Service 线程继承自 base::Thread ,因此它内部也包含了 MessageLoop(每一个 base::Thread 类创建出来的线程都有一个 MessageLoop)。Worker 线程是 TaskScheduler 直接使用 PlatformThread::Create*() 方法创建出来的,因此它不包含 MessageLoop。你可以使用 base::PostTask*() 全局方法来向线程池 Post 任务。

TaskScheduler一般用法如下:

 1 #include <base/logging.h>
 2 #include <base/message_loop/message_loop.h>
 3 #include <base/task/post_task.h>
 4 #include <base/task/task_scheduler/task_scheduler.h>
 5 
 6 void Hello()
 7 {
 8     LOG(INFO)<<"hello,demo!";
 9 }
10 
11 int main(int argc,char** argv)
12 {
13   // 初始化线程池,会创建新的线程,在新的线程中会创建消息循环 MessageLoop
14   base::TaskScheduler::CreateAndStartWithDefaultParams("Demo");
15 
16   // 通过以下方法创建任务
17   base::PostTask(FROM_HERE, base::BindOnce(&Hello));
18   // 或者通过创建新的TaskRunner来创建任务,TaskRunner可以控制任务执行的顺序以及是否在同一个线程中运行
19   scoped_refptr<base::TaskRunner> task_runner_ =
20     base::CreateTaskRunnerWithTraits({base::TaskPriority::USER_VISIBLE});
21   task_runner_->PostTask(FROM_HERE,base::BindOnce(&Hello));
22 
23   // 不能使用以下方法创建任务,会导致程序崩溃,因为当前线程没有创建消息循环
24   //base::MessageLoopCurrent::Get()->task_runner()->PostTask(FROM_HERE, base::BindOnce(&Hello));
25 
26   // 由于线程池默认不会阻塞程序运行,因此这里为了看到结果使用getchar()阻塞主线程。当前进程中共有4个线程,1个主线程,1个线程池Service线程,2个Worker线程。
27   getchar();
28 
29   return 0;
30 }

 

5. TaskScheduler 的运行流程

 

 6. TaskScheduler 的类图

 

 7. 总结

  • 每一个 base::Thread 线程都拥有一个 MessageLoop 用来进行任务调度;
  • 主线程如果需要消息循环,需要自行创建MessageLoop ;
  • 由于 MessageLoop 中维护有 TaskRunner,因此你可以通过获取该线程的 TaskRunner 来给该线程 Post 任务;
  • 线程池使用更底层的 PlatformThread::Create*() 来直接创建线程,从而避免每个线程都有 MessageLoop;
  • 线程池中的任务是通过 base::PostTask*() 创建的;
  • 线程池使用名为 TaskSchedulerSe 的线程来调度需要延时的任务,不需要延迟的任务会直接放入线程池的任务任务队列;
  • 通过 TaskRunner 的 PostTask*() 方法会将任务Post到 TaskRunner 所在的 MessageLoop
  • 通过 base::PostTask*() 全局方法默认会将任务Post到线程池中;

 

8. 参考文献

标签:task,MessageLoop,创建,详解,线程,base,include,Chromium
From: https://www.cnblogs.com/rmb999/p/17784184.html

相关文章

  • Spring MVC入口Servlet详解(HttpServletBean,FrameworkServlet,DispatcherServlet )
    SpringMVC中DispatcherServlet前端控制器是web服务器的入口,那么它是怎么样进行初始化的,是怎么样进行工作?继承关系1.HttpServletBean主要做一些初始化的工作,将web.xml中配置的参数设置到Servlet中。比如servlet标签的子标签init-param标签中配置的参数。2.FrameworkServlet将Serv......
  • DispatcherServlet初始化顺序详解
    1. Web容器启动时将调用HttpServletBean的init方法publicabstractclassHttpServletBeanextendsHttpServletimplementsEnvironmentAware{@Overridepublicfinalvoidinit()throwsServletException{//省略部分代码//1、如下代码的作用是将Serv......
  • Java使用多线程异步执行批量更新操作方法
    一、核心技术Java提供了Executor框架来实现多线程任务的执行。我们可以通过创建ExecutorService对象来管理线程池,然后将任务提交给这个线程池执行。Executor框架的优点在于,它可以自动管理线程数量,以最大化利用CPU和内存资源。二、具体实现方法1、创建一个数据更新任务类,实现Run......
  • ScyllaDB详解
    2ScyllaDB号称下一代NoSQL,C++编写充分利用Linux底层原语优势,利用现代多核、多处理器NUMA服务器硬件,卓越性能,API兼容Cassandra和DynamoDB:支持和Cassandra一样的CQL查询语言和驱动,一样的SSTable存储格式同样支持和DynamoDB一样的JSON-style查询和驱动2.1......
  • 一文详解|支付宝小程序跳转(超详细版)
    开发过程中经常遇到支付宝小程序跳转的问题,这里总结一下支付宝小程序跳转的常见场景和方式,希望可以对大家有所帮助。话不多说,上干货!     支付宝小程序跳转的三种行为支付宝小程序跳转可以拆分为三种行为,即:外部跳转支付宝小程序支付宝小程序内部页面之间跳转......
  • Makefile基础使用和实战详解
    一、基础Makefile其实只是一个指示make程序如何为我们工作的命令文件,我们说Makefile其实是在说make。而对于项目来说,Makefile是指软件项目的编译环境。Makefile的好坏对于项目开发有些什么影响呢?设计得好的Makefile,当我们重新编译时,只需编译那些上次编译成功后修改过的......
  • Go语言代码断行规则详解
    本文深入探讨了Go语言中代码断行的各个方面,从基础概念到实际应用实践。关注【TechLeadCloud】,分享互联网架构、云服务技术的全维度知识。作者拥有10+年互联网服务架构、AI产品研发经验、团队管理经验,同济本复旦硕,复旦机器人智能实验室成员,阿里云认证的资深架构师,项目管理专业......
  • 开源利器推荐:美团动态线程池框架的接入分享及效果展示
    一款美团动态线程池的框架DynamicTp终于开源了,接入简单,侵入性也不强,支持的接入方式也多,总体还是很不错的。我认为尤其适合中小企业SpringBoot应用接入,是一款线程池监控的利器。前言蛮早前有些过关于线程池的使用及参数的一些参考配置,有兴趣的可以翻看以前的博文,但终......
  • 杰哥教你面试之一百问系列:java多线程
    java多线程是java面试中的高频问题,如何才能在面试中脱颖而出呢?熟读这里的一百个java多线程面试问题即可。1.什么是线程?什么是进程?回答:线程是操作系统能够进行调度的最小执行单位,它包含在进程中,共享进程的资源。进程是一个正在执行中的程序,它包含了代码、数据和系统资源。一个进程......
  • jenkins 原理篇——pipeline流水线 声明式语法详解
    大家好,我是蓝胖子,相信大家平时项目中或多或少都有用到jenkins,它的piepeline模式能够对项目的发布流程进行编排,优化部署效率,减少错误的发生,如何去写一个pipeline脚本呢,今天我们就来简单看看pipeline的语法。先拿一个helloworld的pipeline脚本举例,我们来看看pipeline脚本的组成......