美文网首页
chromium源码学习——线程池(上)

chromium源码学习——线程池(上)

作者: 丑角的晨歌 | 来源:发表于2018-09-03 21:25 被阅读0次

以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。

相关文章

网友评论

      本文标题:chromium源码学习——线程池(上)

      本文链接:https://www.haomeiwen.com/subject/fkqvwftx.html