以win32平台下chromium62版本代码为准。
base::TaskRunner类,主要有以下几个实现:
base::MessageLoopTaskRunner,任务投放到单个线程的消息队列;
base::WorkerPoolTaskRuner,任务最终投放到windows提供的线程池中;
base::SchedulerSequencedTaskRunner,任务投放到chrome的TaskScheduler,可以认为是chrome自行实现的一个线程池;
关于chromium的消息队列,网上已经大把文章写过了,所以这里就分析后面两个。
首先看WorkerPoolTaskRunner,这个比较简单,利用了Windows的线程池API实现的:QueueUserWorkItem。系统会自动创建一个线程池来执行你的任务:一个原型如下的函数:
DWORD WINAPI WorkItemFunc(PVOID pvContext);
关于这个API就摘抄一点《Windows核心编程》上的说明吧:
系统会自动为你的进程创建一个线程池,线程池中的一个线程将应用你的函数。另外,当该线程处理完客户机的请求之后,该线程并不立即被销毁,而是返回线程池,这样它就可以准备处理已经排队的任何其他工作项目。你的应用程序的运行效率可能会变得更高,因为不必为每个客户机请求创建和撤销线程。另外,由于线程与完成端口相关联,因此可以同时运行的线程数量限制为CPU数量的两倍。这就减少了线程的上下文转移的开销。
再回到WorkerPoolTaskRunner,这个实现不支持投递延时任务。它的任务最终会到达WorkerPool,也就是对QueueUserWorkItem的一个封装。在WorkerPool的注释中是这样写的:由于浏览器关闭时不会等待WorkerPool中的线程退出,所以里面的运行的任务要十分小心:因为可能在退出浏览器时它们里面使用的一些实例已经被销毁了。在chromium中,像SSL的证书校验,DNS域名解析等任务是在 WorkerPool中运行的。
由于base::Bind已经提供了将任务封装为对象的方法,所以WorkerPool的封装就变成十分简单的事情:将封装好的Task对象作为参数,让线程池通过一个wrapper函数来执行即可,核心实现如下:
DWORD CALLBACK WorkItemCallback(void* param) {
PendingTask* pending_task = static_cast<PendingTask*>(param);
......
std::move(pending_task->task).Run();
......
delete pending_task;
return 0;
}
// Takes ownership of |pending_task|
bool PostTaskInternal(PendingTask* pending_task, bool task_is_slow) {
......
if (task_is_slow)
flags |= WT_EXECUTELONGFUNCTION;
if (!QueueUserWorkItem(WorkItemCallback, pending_task, flags)) {
DPLOG(ERROR) << "QueueUserWorkItem failed";
delete pending_task;
return false;
}
return true;
}
投递的任务经过几次简单的中转后最终到达PostTaskInternal,调用QueueUserWorkItem投递到系统线程池。系统的线程池会调用WorkItemCallback,从参数中取出之前投递的任务,并执行之。
下篇分析SequencedTask。
网友评论