您的位置:首页 > 编程语言

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来执行回调任务。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: