Chrome中的消息循环

主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦。主要是线程之间要通信,要切线程,要同步,各种麻烦。我本身的工作经历决定了也没有太多的工作经验,所以chrome的messageloop可以说是我用到的第一个成熟的线程消息封装库,用的很简单,舒服。主要涉及MessageLoop和MessagePump这两个类系。

以前不太清楚chrome当时在设计这两个类时是如何分工的,今天又看了一下代码,有了点感觉。MessagePump主要用来做消息循环, 与操作系统等平台相关的部分都在MessagePump类里, 针对不同的平台有不同的实现,对messageloop封装了平台的不一致性。而MessageLoop主要是处理chrome自己的task机制的,这一部分。我们以windows平台来进行代码分析, MessagePump中的DoRunLoop是每个线程进行消息循环处理的地方。

void MessagePumpForUI::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)
    // ProcessNextWindowsMessage(), 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 = ProcessNextWindowsMessage();
    if (state_->should_quit)
      break;

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

    more_work_is_plausible |=
        state_->delegate->DoDelayedWork(&delayed_work_time_);
    // If we did not process any delayed work, then we can assume that our
    // existing WM_TIMER if any will fire when delayed work should run.  We
    // don't want to disturb that timer if it is already in flight.  However,
    // if we did do all remaining delayed work, then lets kill the WM_TIMER.
    if (more_work_is_plausible && delayed_work_time_.is_null())
      KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
    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.
  }
}

这块儿代码通过一个for的死循环来维持线程的运行, 同时进行系统消息的处理和task的处理,从代码看可以分为循环可以分为如下几个部分:伪码描述: 

for(;;)
{
  处理windows系统消息

  执行task队列中的一个task

  执行delayedTask队列中的一个task。

  if(还有其他任务(more_work_is_pausiable)), 
    continue;
 else
   挂起线程,等待消息进行唤醒
}

下面分别从几部分进行分析:

处理windows消息 

循环先从Windows的消息队列中提取下一条消息进行处理。

bool more_work_is_plausible = ProcessNextWindowsMessage();
bool MessagePumpForUI::ProcessNextWindowsMessage() {
  // If there are sent messages in the queue then PeekMessage internally
  // dispatches the message and returns false. We return true in this
  // case to ensure that the message loop peeks again instead of calling
  // MsgWaitForMultipleObjectsEx again.
  bool sent_messages_in_queue = false;
  DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE);
  if (HIWORD(queue_status) & QS_SENDMESSAGE)
    sent_messages_in_queue = true;

  MSG msg;
  if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
    return ProcessMessageHelper(msg);

  return sent_messages_in_queue;
}

 在processNextWindowsMessage函数中主要处理 window的窗口消息,它的返回值 表示输入队列中是否还有其他消息待处理,这样可以避免多调用一次MsgWaitForMultipleObjectsEx。

具体的ProcessMessageHelper代码如下,单独的WM_QUIT来进行单独推出处理。

bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
  TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper",
               "message", msg.message);
  if (WM_QUIT == msg.message) {
    state_->should_quit = true;
    PostQuitMessage(static_cast<int>(msg.wParam));
    return false;
  }

  // While running our main message pump, we discard kMsgHaveWork messages.
  if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_)
    return ProcessPumpReplacementMessage();

  if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode))
    return true;

  WillProcessMessage(msg);

  if (!message_filter_->ProcessMessage(msg)) {
    if (state_->dispatcher) {
      if (!state_->dispatcher->Dispatch(msg))
        state_->should_quit = true;
    } else {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  DidProcessMessage(msg);
  return true;
}

执行task 

处理了一个windows消息后, 然后通过MessagePump::Delegate的接口,调用MessageLoop的DoWork操作,来处理task队列。

 more_work_is_plausible |= state_->delegate->DoWork(); 
bool MessageLoop::DoWork() {
  if (!nestable_tasks_allowed_) {
    // Task can't be executed right now.
    return false;
  }

  for (;;) {
    ReloadWorkQueue();
    if (work_queue_.empty())
      break;

    // Execute oldest task.
    do {
      PendingTask pending_task = work_queue_.front();
      work_queue_.pop();
      if (!pending_task.delayed_run_time.is_null()) {
        AddToDelayedWorkQueue(pending_task);
        // If we changed the topmost task, then it is time to reschedule.
        if (delayed_work_queue_.top().task.Equals(pending_task.task))
          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
      } else {
        if (DeferOrRunPendingTask(pending_task))
          return true;
      }
    } while (!work_queue_.empty());
  }

  // Nothing happened.
  return false;
}

这个函数中通过循环来找到一个处理当前taskQueue的一个task进行执行, 将delayed的task 存入DelayedWorkQueue.

 执行DelayedTask

bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) {
  if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) {
    recent_time_ = *next_delayed_work_time = TimeTicks();
    return false;
  }

  // When we "fall behind," there will be a lot of tasks in the delayed work
  // queue that are ready to run.  To increase efficiency when we fall behind,
  // we will only call Time::Now() intermittently, and then process all tasks
  // that are ready to run before calling it again.  As a result, the more we
  // fall behind (and have a lot of ready-to-run delayed tasks), the more
  // efficient we'll be at handling the tasks.

  TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time;
  if (next_run_time > recent_time_) {
    recent_time_ = TimeTicks::Now();  // Get a better view of Now();
    if (next_run_time > recent_time_) {
      *next_delayed_work_time = next_run_time;
      return false;
    }
  }

  PendingTask pending_task = delayed_work_queue_.top();
  delayed_work_queue_.pop();

  if (!delayed_work_queue_.empty())
    *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time;

  return DeferOrRunPendingTask(pending_task);
}

 比对时间,如果到了delayedtask的执行时机,执行delayed task。

 挂起等待用户输入消息

void MessagePumpForUI::WaitForWork() {
  // Wait until a message is available, up to the time needed by the timer
  // manager to fire the next set of timers.
  int delay = GetCurrentDelay();
  if (delay < 0)  // Negative value means no timers waiting.
    delay = INFINITE;

  DWORD result;
  result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
                                       MWMO_INPUTAVAILABLE);

  if (WAIT_OBJECT_0 == result) {
    // A WM_* message is available.
    // If a parent child relationship exists between windows across threads
    // then their thread inputs are implicitly attached.
    // This causes the MsgWaitForMultipleObjectsEx API to return indicating
    // that messages are ready for processing (Specifically, mouse messages
    // intended for the child window may appear if the child window has
    // capture).
    // The subsequent PeekMessages call may fail to return any messages thus
    // causing us to enter a tight loop at times.
    // The WaitMessage call below is a workaround to give the child window
    // some time to process its input messages.
    MSG msg = {0};
    DWORD queue_status = GetQueueStatus(QS_MOUSE);
    if (HIWORD(queue_status) & QS_MOUSE &&
        !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
      WaitMessage();
    }
    return;
  }

  DCHECK_NE(WAIT_FAILED, result) << GetLastError();
}

  通过MsgWaitForMultiPleObjectsEx来进行挂起等待。

语言表达能力不行,不知到说清楚没有 。 不清楚在补吧 

原文地址:https://www.cnblogs.com/kwliu/p/3368946.html