【问题标题】:How can synchronous event demultiplexing be a solution to busy waiting?同步事件多路分解如何成为忙等待的解决方案?
【发布时间】:2016-06-17 06:28:22
【问题描述】:

我正在尝试了解同步事件多路分离如何成为忙碌等待的解决方案。

假设有 3 个 IO 操作,我们有一个不断循环的代码,以检查 3 个操作中的任何一个是否有数据可供读取。

    arry = [event1 , event2 , event3]

    while(arry is not empty)
    {

        for(i = 0 ; i <= 2 ; i++)
        {
            if(arry[i] has something to read)
            {
                read data;
            }
            else
            {
               continue to next i;
            }

            if(arry[i] read has finished){ 
                remove i from arry
             }
        }
    }

上面的伪代码做了一个忙等待。

现在在同步事件多路分离或反应器模式中,事件侦听器可以在事件发生时对其进行响应。但是事件监听器如何在不忙于等待的情况下做到这一点呢?

【问题讨论】:

  • 我不得不承认我从未听说过“同步事件多路分解”。您能否指出一些描述它的资源,或者在您的问题中放置一些示例(伪)代码?
  • 你不会做任何与 JavaScript 事件同步的事情。但这并不意味着反应器模式也不能异步工作 - 您无需在事件到达之间忙于等待,您只需在每个等待的事件上安排循环代码。

标签: javascript node.js dom-events


【解决方案1】:

进程是已执行的计算机程序的实例(执行任务或模块)。在单个进程中,我们可以有多个称为线程的组件。您可以将线程可视化为待办事项列表,其中包含一些需要由计算机 CPU 执行的指令。将线程视为工作者。

假设您有一个 10 层的公寓。而且你有一个有指示的工人,它必须敲开每一扇门才能收集数据。在忙碌的等待中,这个可怜的工人从一楼开始,会敲门检查数据是否准备好接收。它会一直到顶层。然后它会在他的路上做同样的过程。这次它会跳过之前已经接收到数据的门。所以它会一直上下运行,直到它接收到来自每扇门的数据。这基本上就是忙等待。

如您所见,使用单线程,我们可以在不阻塞的情况下处理不同的资源。但是这样效率不高,它会使 CPU 消耗大量内存并导致上下文切换,这对 CPU 来说是一项额外的工作。此外,线程将大部分时间浪费在等待上,而它在等待时 CPU 仍在工作。

为什么事件解复用是一种解决方案或更有效的方法?

代替所有这些繁重的工作,我们的单线程不会上下运行。它不再是一个工人,它现在是一个管理者,并得到了使用 C++ 强大功能的 libuv 库的额外支持。 Libuv 将使用事件解复用器收集所有数据,解复用器将包括哪些数据来自哪个门以及如何处理这些数据,然后它将必要的数据传递给事件队列,然后事件队列将控制权传递给事件循环。现在事件循环的工作只是检查是否有等待事件,如果有,它会将其推送到调用堆栈,这是函数被执行的地方。

事件多路分解器被称为同步,因为它不会同时敲两扇门。

用简单的方式解释这个复杂的主题真的很难。希望对你有帮助。

【讨论】:

    【解决方案2】:

    这不是它在 JS 中的工作方式。

    当您发出一些可能触发异步通知的请求时,您必须提供一个回调,该回调将以请求结果作为参数被调用。

    你的伪代码应该是这样的:

    // request completion callback
    function handle_request_notification (request status and associated data)
    {
        store request data wherever you like
        if all requests are complete (without error), do what you want to do
        (now that you have a complete set of data)
    }
    
    // issuing requests
    do_request (whatever1, handle_request_notification)
    do_request (whatever2, handle_request_notification)
    do_request (whatever3, handle_request_notification)
    

    所以基本上它是处理异步请求的库,当实际有要读取的内容(或请求失败)时,它会激活所需的代码。

    每个特定的库都有自己的方法。

    您可以先看看 Ajax,它可能是所有 JS 异步库之母。

    或者,如果您想要更简单的东西来学习基础知识,只需使用计时器 :)

    【讨论】:

      【解决方案3】:

      忙于等待会浪费 CPU 时间检查大部分时间不可用的资源。另一方面,事件解复用器迭代事件,因此它仅在有数据准备好处理时进行迭代。

      【讨论】:

        【解决方案4】:

        在你上面的代码中,for循环会反复迭代,会浪费CPU资源。

        另一方面,反应器模式代码如下所示:

        array = [event1 , event2 , event3];
        while(events = demultiplexer.watch(array)) {
           events.forEach(event => {
               \\ Data must be available at this point
               process(event.data);
               array.remove(event);
           })
        }
        

        此调用events = demultiplexer.watch(array) 是同步的,因此它将阻塞线程,直到任何资源可用为止。当其中一个资源可用时,解复用器将返回它,然后才会执行 for 循环。

        所以简而言之,忙等待会浪费 CPU 时间来迭代大多数时间不可用的资源,而使用反应器模式,它只会迭代可用资源。

        【讨论】:

          猜你喜欢
          • 2012-03-31
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-02-05
          • 2011-05-05
          • 2014-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多