Chrome源代码分析之线程模型续(十二)
2013-04-28 16:10
429 查看
在看看这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来执行回调任务。
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来执行回调任务。
相关文章推荐
- Chrome源代码分析之进程和线程模型(三)
- Chrome源代码分析之线程模型(十一)
- Chrome源代码分析之进程模型(八)
- Memcached源码分析之线程模型
- Memcached线程模型分析
- Android应用程序线程消息循环模型分析(3)
- Mozilla Firefox Extension扩展 内幕 教程 源代码分析 安装过程分析(XPInstall,xpcom,rdf,xpi,chrome,manifest)
- quartz源码分析——执行引擎和线程模型
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- Memcached源码分析(线程模型)
- quartz2.2源码分析3-线程模型
- Android应用程序线程消息循环模型分析(4)
- netty源码分析 之十三 线程模型
- Java、C#线程模型分析对比
- CUDA 线程执行模型分析 GPU 推荐
- 源码分析mycat1.6之网络篇---前端线程模型下篇(读写事件篇)
- Android应用程序线程消息循环模型分析
- PostgreSQL的 initdb 源代码分析之十二
- Hbase WAL线程模型源码分析