Chrome源代码分析之线程模型续(十二)

来源:互联网 发布:网络细分市场评估方法 编辑:程序博客网 时间:2024/05/18 02:48

在看看这3个MessagePumpWin子类的区别,首先是初始化的过程不同.

MessagePumpForUI会调用RegisterClassEx注册一个窗口类,然后通过这个类创建一个窗口,并且注册WndProcThunk作为窗口消息的处理函数,这是典型的窗口线程的做法。

 MessagePumpForIO则调用port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1));创建了一个完成端口对象,由于IO线程要集中处理所有的I/O请求,包括所有网络I/O和进程间通信,以及本地文件I/O,特别是在访问一些复杂的页面的同时还会进行本地缓存,这个并发量在某些时候还是很高的,完成端口这个被微软称为最佳的高性能及可伸缩式I/O模型用在这里是非常适合的

MessagePumpDefault在初始化的时候几乎什么也没有做。


除了初始化的过程不同,每种线程处理消息的流程也不一样,这是他们的显著区别。

 MessagePumpForIO的消息循环是这样的:

void MessagePumpForIO::DoRunLoop() {
  for (;;) {
    // If we do any work, we may create more messages etc., and more work may
    // possibly be waiting in another task group.  When we (for example)
    // WaitForIOCompletion(), there is a good chance there are still more
    // messages waiting.  On the other hand, when any of these methods return
    // having done no work, then it is pretty unlikely that calling them
    // again quickly will find any work to do.  Finally, if they all say they
    // had no work, then it is a good time to consider sleeping (waiting) for
    // more work.

    bool more_work_is_plausible = state_->delegate->DoWork();
    if (state_->should_quit)
      break;
    more_work_is_plausible |= WaitForIOCompletion(0, NULL);
    if (state_->should_quit)
      break;
    more_work_is_plausible |=
        state_->delegate->DoDelayedWork(&delayed_work_time_);
    if (state_->should_quit)
      break;

    if (more_work_is_plausible)
      continue;

    more_work_is_plausible = state_->delegate->DoIdleWork();
    if (state_->should_quit)
      break;

    if (more_work_is_plausible)
      continue;


    WaitForWork();  // Wait (sleep) until we have work to do again.
  }
}


  // Nothing happened.
  return false;
}



可以看到,I/O线程首先调用DoWork(),DoWork()是什么的呢,在I/O线程中有一个work_queue_,里面保存的是PendingTask,这个结构体里面最重要的成员就是一个Task指针。所谓TASK,跟字面意思差不多,代表着一个任务,其具体内容,后面专门分析。再继续深入分析,会进入到MessageLoop的RunTask函数中,这里,task会调用自己的通用方法RUN()来执行具体的任务。

总结上面的分析,I/O线程首先执行普通task,接着调用WaitForIOCompletion(),后面是延迟task,空闲task,最后调用WaitForWork(),让线程阻塞在GetQueuedCompletionStatus这里。

WaitForIOCompletion()又干的是什么活呢?WaitForIOCompletion处理的是调用RegisterIOHandler注册到当前完成端口的所有句柄上面发生的I/O,这里的句柄主要是以CreateFile方式创建的句柄,目前知道的主要是文件句柄,命名端口句柄。而网络I/O则没有注册给完成端口来执行,而是注册成一个特殊的TASK:Watch来执行,Watch在前面的socket章节已经分析过,这里不再敷述。

看看WaitForIOCompletion的代码:


bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
  IOItem item;
  if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
    // We have to ask the system for another IO completion.
    if (!GetIOItem(timeout, &item))
      return false;

    if (ProcessInternalIOItem(item))
      return true;
  }

  if (item.context->handler) {
    if (filter && item.handler != filter) {
      // Save this item for later
      completed_io_.push_back(item);
    } else {
      DCHECK_EQ(item.context->handler, item.handler);
      WillProcessIOEvent();
      item.handler->OnIOCompleted(item.context, item.bytes_transfered,
                                  item.error);
      DidProcessIOEvent();
    }
  } else {
    // The handler must be gone by now, just cleanup the mess.
    delete item.context;
  }
  return true;
}


GetIOItem的作用就是调用GetQueuedCompletionStatus获得与具体I/O对应的句柄和事件上下文。与以前的一些完成端口实例不同,这里句柄结构和事件上下文其实是一回事,因为他们定义在了一个结构体里面。

  struct IOContext {
    OVERLAPPED overlapped;
    IOHandler* handler;
  };

接着经过一系列判断之后会调用IOHandler的成员函数OnIOCompleted来执行回调任务。

原创粉丝点击