zoukankan      html  css  js  c++  java
  • 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来进行挂起等待。

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

  • 相关阅读:
    VBScript 语法函数(ScriptControl 控件)
    VB 读和写配置文件
    【转】VB控件和对象:ScriptControl 控件
    VB ADODB中的RecordSet.Open打开记录的两个参数adOpenKeyset、adLockBatchOptimistic的详细说明
    JavaScript的replace方法与正则表达式结合应用讲解
    jQuery DatePicker
    PHP date与gmdate的区别及如何修改PHP的默认时区
    jQuery获取Select选择的Text和 Value(转)
    JavaScript字符串函数大全
    CONFLICTING TYPES的原因
  • 原文地址:https://www.cnblogs.com/kwliu/p/3368946.html
Copyright © 2011-2022 走看看