Chrome的线程体系

网上已经有网友duguguiyu针对Chrome的线程体系做了很专业的描述了,应该说从原理上已经很完整了。本文主要在网友的基础上从代码实现的角度上进行一些补充和分析。
提到线程,我想大家关注的无非几点:线程的消息循环机制、多线程同步机制,线程间通信机制。
本文也是从大家关注的几点来重点描述。
在Chrome的代码中,与线程相关的文件主要在\base\base.vcproj中。
主要包含了以下相关文件:
task.h 线程执行的任何任务都是以task对象方式传递,有不同类型的task。
message_pump.h
message_pump_default.h
message_pump_default.cc
message_pump_win.h
message_pump_win.cc 消息泵类,消息调度,分发处理。
message_loop.h
message_loop.cc 消息循环机制,基本上每一个线程都有自己的消息循环,接收来自其他线程、UI甚至系统的消息。
thread.h
thread.cc 线程虚类类。Thread类在操作系统上层做了抽象,本身与平台无关
platform_thread.h
platform_thread_win.cc Windows平台下线程的相关方法。在Windows平台下是CreateThread方法。
thread_local.h
thread_local.cc
thread_local_storage.h
thread_local_storage_win.cc 线程本地存储机制的实现(TLS)

其他还有一些辅助类,可以不用太关注,比如智能指针、一些简单的工具类等。我们重点分析上述的代码。


总体来说,Chrome的线程实现,主要运用了Command、Bridge和Observer三种模式。

线程类(Thread)的结构和简单流程

我们观看一下Thread.h中对Thread的定义

  1. //线程的抽象类
  2. // PlatformThread是底层的线程辅助类,实现不同平台下的真正的线程实现。
  3. // PlatformThread: Delegate,代理接口类,包含了ThreadMain函数。
  4. //由Thread的子类实现,执行由PlatformThread传递到线程的回调函数中
  5. classThread : PlatformThread::Delegate {
  6. public:
  7. structOptions {
  8. //线程消息循环类型有三种,Default(普通的后台线程),UI,IO线程
  9. //不同线程消息循环处理消息的方式有一些差异。
  10. MessageLoop::Type message_loop_type;
  11. //制定线程堆栈大小
  12. size_t stack_size;
  13. Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {}
  14. Options(MessageLoop::Type type, size_t size)
  15. : message_loop_type(type), stack_size(size) {}
  16. };
  17. explicitThread(constchar*name);

  18. virtual~Thread();

  19. boolStart();

  20. boolStartWithOptions(constOptions& options);

  21. voidStop();

  22. voidStopSoon();

  23. //获取消息循环实例,每一个线程的核心消息循环所在
  24. MessageLoop* message_loop()const{returnmessage_loop_; }

  25. conststd::string &thread_name() {returnname_; }

  26. PlatformThreadHandle thread_handle() {returnthread_; }

  27. PlatformThreadId thread_id()const{returnthread_id_; }

  28. boolIsRunning()const{returnthread_id_ != 0; }

  29. protected:
  30. virtualvoidInit() {}

  31. virtualvoidCleanUp() {}

  32. staticvoidSetThreadWasQuitProperly(boolflag);

  33. staticboolGetThreadWasQuitProperly();

  34. private:
  35. //实现了Platform_Thread类的Delegate接口
  36. virtualvoidThreadMain();

  37. boolthread_was_started()const{returnstartup_data_ != NULL; }

  38. structStartupData;

  39. StartupData* startup_data_;

  40. PlatformThreadHandle thread_;

  41. MessageLoop* message_loop_;

  42. PlatformThreadId thread_id_;
  43. // The name of the thread. Used for debugging purposes.
  44. std::string name_;

  45. friendclassThreadQuitTask;
  46. DISALLOW_COPY_AND_ASSIGN(Thread);
  47. };

//平台相关的线程函数,不同平台有不同的实现机制。

classPlatformThread {

  1. public:

  2. staticPlatformThreadId CurrentId();

  3. staticvoidYieldCurrentThread();

  4. staticvoidSleep(intduration_ms);

  5. staticvoidSetName(constchar* name);
  6. classDelegate {
  7. public:
  8. virtual~Delegate() {}
  9. virtualvoidThreadMain() = 0;
  10. };
  11. staticboolCreate(size_t stack_size, Delegate*delegate,
  12. PlatformThreadHandle* thread_handle);
  13. staticboolCreateNonJoinable(size_t stack_size, Delegate*delegate);
  14. staticvoidJoin(PlatformThreadHandle thread_handle);
  15. private:
  16. DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
  17. };


Thread是一个与平台无关的线程抽象类。真正线程创建的相关函数在PlatformThread类中处理,而PlatformThread类的实现根据不同的操作系统又有不同的实现。在Windows平台下实现的是
platform_thread_win.cc。

我们跟踪一下线程函数的执行流程:

下面是platform_thread_win.cc中的实现:

  1. DWORD__stdcallThreadFunc(void* closure) {
  2. PlatformThread::Delegate*delegate=
  3. static_cast<PlatformThread::Delegate*>(closure);
  4. delegate->ThreadMain();
  5. returnNULL;
  6. }
  7. boolPlatformThread::Create(size_t stack_size, Delegate*delegate,
  8. PlatformThreadHandle* thread_handle) {
  9. unsignedintflags = 0;
  10. if(stack_size > 0 && win_util::GetWinVersion() >= win_util::WINVERSION_XP) {
  11. flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
  12. }else{
  13. stack_size = 0;
  14. }
  15. *thread_handle = CreateThread(
  16. NULL, stack_size, ThreadFunc,delegate, flags, NULL);
  17. return*thread_handle != NULL;
  18. }


1.*thread_handle = CreateThread(

NULL, stack_size, ThreadFunc,delegate, flags, NULL);

ThreadFunc是线程函数,线程的执行体从此开始。

2. ThreadFunc函数实际调用的是delegate->ThreadMain();函数,由于Thread类实现了PlatformThread:Delegate代理类, 因此实际上调用的是Thread类的ThreadMain函数。

3.观察Thread类的ThreadMain函数实现,ThreadMain函数实际启动了消息循环MessageLoop,MessageLoop类接收不同的任务,并进行处理。

voidThread::ThreadMain() {

//创建消息循环.

MessageLoop message_loop(startup_data_->options.message_loop_type);

thread_id_ = PlatformThread::CurrentId();

PlatformThread::SetName(name_.c_str());

message_loop.set_thread_name(name_);

message_loop_ = &message_loop;

//进一步初始化

Init();

//设置启动完毕信号量信号

startup_data_->event.Signal();

//消息循环开始启动,线程阻塞于此

message_loop.Run();

//线程结束,收尾工作

CleanUp();

DCHECK(GetThreadWasQuitProperly());

message_loop_ = NULL;

thread_id_ = 0;

}

4.我们进一步跟踪一下MessageLoop的Run函数实现。可以发现实际上真正的线程调度工作是MessagePump类来实现的。

  1. voidMessageLoop::Run() {
  2. AutoRunState save_state(this);
  3. RunHandler();
  4. }
  5. voidMessageLoop::RunHandler() {
  6. #ifdefined(OS_WIN)
  7. if(exception_restoration_) {
  8. LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
  9. __try{
  10. RunInternal();
  11. }__except(SEHFilter(current_filter)) {
  12. }
  13. return;
  14. }
  15. #endif
  16. RunInternal();
  17. }
  1. voidMessageLoop::RunInternal() {
  2. DCHECK(this== current());
  3. StartHistogrammer();
  4. #ifdefined(OS_WIN)
  5. //暂时我们可以过滤掉这个分支
  6. if(state_->dispatcher) {
  7. pump_win()->RunWithDispatcher(this, state_->dispatcher);
  8. return;
  9. }
  10. #endif
  11. pump_->Run(this);
  12. }

5.由于MessagePump类是一个虚类,其根据不同的线程类型有不同的调度方法,我们先看看MessagePumpDefault类怎么实现的。

voidMessagePumpDefault::Run(Delegate*delegate) {

DCHECK(keep_running_) <<"Quit must have been called outside of Run!";

for(;;) {

ScopedNSAutoreleasePool autorelease_pool;

booldid_work =delegate->DoWork();

if(!keep_running_)

break;

did_work |=delegate->DoDelayedWork(&delayed_work_time_);

if(!keep_running_)

break;

if(did_work)

continue;

did_work =delegate->DoIdleWork();

if(!keep_running_)

break;

if(did_work)

continue;

if(delayed_work_time_.is_null()) {

event_.Wait();

}else{

TimeDelta delay = delayed_work_time_ - Time::Now();

if(delay > TimeDelta()) {

event_.TimedWait(delay);

}else{

// It looks like delayed_work_time_ indicates a time in the past, so we

// need to call DoDelayedWork now.

delayed_work_time_ = Time();

}

}

// Since event_ is auto-reset, we don't need to do anything special here

// other than service each delegate method.

}

keep_running_ =true;

}


这个函数实现了任务的调度算法,真正的工作又其实是MessageLoop类实现的(MessageLoop类实现了MessagePump:Delegate类)。
MessageLoop类包含了多个任务队列(即时执行任务,延时执行任务,Idle任务),并提供接口用户提交任务,

这个函数的实现有一些灵巧的调度算法包含在里面,我们将在第二节的消息循环中去详细讲解。

从上面的分析来看,Thread类并不真正的实现任何业务功能,仅仅实现了任务的调度,线程的启动、停止等功能,线程需要执行的工作都是由用户来提交的。这个任务就是Task类的提交的,这里用到了典型的Command模式。

线程实现中的一些模式

下面的图是设计模式中Command模式的典型结构图:


Command设计模式

引用《设计模式》中的话:

把一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。

引用网友Venus的话

Command模式
Command 模式,是一种看上去很酷的模式,传统的面向对象编程,我们封装的往往都是数据,在Command模式下,我们希望封装的是行为。这件事在函数式编程中很正 常,封装一个函数作为参数,传来传去,稀疏平常的事儿;但在面向对象的编程中,我们需要通过继承、模板、函数指针等手法,才能将其实现。。。
应用Command模式,我们是期望这个行为能到一个不同于它出生的环境中去执行,简而言之,这是一种想生不想养的行为。我们做Undo/Redo的时 候,会把在任一一个环境中创建的Command,放到一个队列环境中去,供统一的调度;在Chrome中,也是如此,我们在一个线程环境中创建了 Task,却把它放到别的线程中去执行,这种寄居蟹似的生活方式,在很多场合都是有用武之地的。。。

对应在Chrome中线程的实现Task实现了Command模式中的Command接口,而类似CancelableTask,DeleteTask,ReleaseTask等类实现了COncreteCommand类。MessageLoop类相当于是一个Receiver,负责接收各种任务,并进行处理。

举一个例子:

Chrome中Command模式

这种设计方法,把线程的调度和实际的任务进行了分离,减少了模块之间的耦合性。同时具有:

1. 与我们传统的实现方式不同,抛弃了Callback函数方式,先注册,以后调用。采用Task方式,具有更多的自由度。

2. 一个Task对象和原先的请求发出者(Invoker)可以有不同的生命期。换言之,原先的请求发出者(Invoker)可能已经不在了,而Task对象本身仍然是活动的。

### 回答1: Python中可以使用多线程来启动Chrome浏览器。我们可以利用selenium库来控制浏览器的行为,并结合多线程来实现同时启动多个Chrome浏览器实例。 首先,我们需要安装selenium库,可以使用pip命令进行安装。安装完成后,我们需要下载对应版本的Chrome浏览器驱动,然后将驱动的路径添加到系统的环境变量中。 接下来,我们可以编写一个多线程的函数,用于启动Chrome浏览器。首先导入selenium库中的webdriver模块,然后在函数中创建一个webdriver对象,设置浏览器的选项,如窗口大小、启动位置等,最后使用get()方法打开要访问的网页。 然后,我们可以编写一个主函数,创建多个线程来分别执行启动浏览器的任务。可以使用threading库中的Thread类来创建线程对象,并使用start()方法来启动线程。 最后,在主函数中使用join()方法使主线程等待所有子线程执行完毕,然后关闭Chrome浏览器。 下面是一个示例代码: ```python import threading from selenium import webdriver def open_browser(): driver = webdriver.Chrome() # 创建Chrome浏览器对象 driver.maximize_window() # 设置浏览器窗口大小为最大化 driver.get("https://www.example.com") # 打开要访问的网页 def main(): threads = [] # 存放线程列表 for i in range(5): # 启动5个线程 t = threading.Thread(target=open_browser) # 创建线程对象 threads.append(t) # 添加到线程列表 t.start() # 启动线程 for t in threads: t.join() # 等待所有线程执行完毕 driver.quit() # 关闭Chrome浏览器 if __name__ == "__main__": main() ``` 以上就是使用Python多线程来启动Chrome浏览器的简单示例。通过多线程的方式,我们可以同时启动多个Chrome浏览器实例,提高程序的执行效率。 ### 回答2: 在Python中启动Chrome浏览器并使用多线程可以通过以下步骤完成。 首先,我们需要安装并导入selenium库,它是一种Web自动化测试工具,可以与Chrome浏览器进行交互。 接下来,我们需要下载Chrome驱动程序,该驱动程序用于控制和操作Chrome浏览器。下载完驱动程序后,我们需要将其添加到系统路径中,以便Python可以找到它。 然后,我们可以使用selenium库的webdriver模块来创建浏览器对象。在创建浏览器对象时,我们可以通过指定Chrome驱动程序的路径来告诉Python要使用Chrome浏览器。 接下来,我们可以使用创建的浏览器对象来打开一个URL,并执行我们想要的操作,例如点击按钮,填写表单等等。 最后,我们可以在一个多线程的环境中启动多个线程,每个线程创建一个浏览器对象并执行相应的操作。 以下是一个示例代码,演示了如何使用多线程启动Chrome浏览器: ```python from selenium import webdriver import threading def open_browser(url): # 创建浏览器对象 driver = webdriver.Chrome("path/to/chromedriver") # 打开URL并执行相应操作 driver.get(url) # 进行其他操作... # 关闭浏览器 driver.quit() # 设置要打开的URL列表 urls = ["https://www.example1.com", "https://www.example2.com", "https://www.example3.com"] # 创建并启动多个线程 threads = [] for url in urls: thread = threading.Thread(target=open_browser, args=(url,)) thread.start() threads.append(thread) # 等待所有线程结束 for thread in threads: thread.join() ``` 在上面的代码中,我们使用了一个开放浏览器的函数`open_browser`,它接受一个URL作为参数并在浏览器中打开该URL。我们创建了一个URL列表,并使用多线程启动多个线程,每个线程都打开一个URL。最后,我们等待所有线程结束。 以上就是使用多线程启动Chrome浏览器的基本步骤和示例代码。希望对你有所帮助! ### 回答3: Python多线程是一种可以在同一程序中同时运行多个线程的机制。通过多线程,我们可以在一个程序中同时执行多个任务,提高程序的运行效率。而启动Chrome是指通过Python脚本来启动Chrome浏览器。 在Python中,我们可以使用threading模块来实现多线程编程。首先,需要导入threading模块,然后使用Thread类创建一个线程对象。接下来,我们可以定义一个函数,作为线程的执行体。在这个函数中,可以编写启动Chrome浏览器的代码。 启动Chrome浏览器可以使用selenium库,它提供了对多种浏览器的支持。通过selenium库的webdriver模块,我们可以创建一个浏览器对象,并在代码中进行相应的操作。 下面是一个简单的示例代码: ``` import threading from selenium import webdriver def start_chrome(): driver = webdriver.Chrome() # 创建Chrome浏览器对象 driver.get("https://www.example.com") # 打开指定网址 # 进行其他操作,如点击按钮、填写表单等 driver.quit() # 关闭Chrome浏览器 # 创建一个线程对象,并指定要执行的函数 thread = threading.Thread(target=start_chrome) # 启动线程 thread.start() ``` 在这个示例中,我们使用threading模块创建了一个线程对象,并指定了要执行的函数为`start_chrome`。然后,使用`start()`方法启动线程线程会自动执行`start_chrome`函数中的代码。 在`start_chrome`函数中,我们首先创建了一个Chrome浏览器对象,然后使用`get()`方法打开了一个网址,并进行其他操作。最后,使用`quit()`方法关闭Chrome浏览器。 总的来说,通过多线程机制可以在Python中同时启动Chrome浏览器,从而实现并发执行多个任务的效果。这样可以提高程序的效率,并且方便进行一些自动化的操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值