【问题标题】:Does an asynchronous call always create/call a new thread?异步调用是否总是创建/调用新线程?
【发布时间】:2010-10-10 13:13:51
【问题描述】:

异步调用总是创建一个新线程吗?

例子:

如果 JavaScript 是单线程的,那么它如何进行异步回发呢?它实际上是阻塞直到它得到回调吗?如果是这样,这真的是异步调用吗?

【问题讨论】:

    标签: multithreading asynchronous


    【解决方案1】:

    这是一个有趣的问题。

    异步编程是一种主要是单线程的编程范例,即“跟随一个连续执行的线程”。

    您指的是 javascript,所以让我们在 Web 浏览器的环境中讨论该语言。 Web 浏览器在每个窗口中运行一个 javascript 执行线程,它处理事件(例如 onclick="someFunction()")和网络连接(例如 xmlhttprequest 调用)。

    <script>
    function performRequest() {
      xmlhttp.open("GET", "someurl", true);
      xmlhttp.onreadystatechange = function() {
        if (xmlhttp.readyState == 4) {
          alert(xmlhttp.responseText);
        }
      }
      xmlhttp.send(sometext);
    }
    </script>
    <span onclick="performRequest()">perform request</span>
    

    (这是一个无效的示例,仅用于演示概念)。

    为了以异步方式执行所有操作,控制线程具有所谓的“主循环”。主循环看起来像这样:

    while (true) {
        event = nextEvent(all_event_sources);
        handler = findEventHandler(event);
        handler(event);
    }
    

    请务必注意,这不是“忙循环”。这有点像一个睡眠线程,等待活动发生。 Activity 可以是来自用户的输入(鼠标移动、按钮单击、打字),也可以是网络活动(来自服务器的响应)。

    所以在上面的例子中,

    1. 当用户点击 span 时,会生成一个 ButtonClicked 事件,findEventHandler() 会在 span 标签上找到 onclick 事件,然后该事件会调用该处理程序。
    2. 当 xmlhttp 请求被创建时,它被添加到事件源的 all_event_sources 列表中。
    3. performRequest() 函数返回后,主循环在 nextEvent() 步骤等待响应。在这一点上,没有什么“阻止”进一步的事件被处理。
    4. 数据从远程服务器返回,nextEvent() 返回网络事件,发现事件处理程序是 onreadystatechange() 方法,调用该方法,并启动 alert() 对话框。

    值得注意的是,alert() 是一个阻塞对话框。当该对话框启动时,无法处理更多事件。我们有一个现成的方法会阻止在该页面的上下文中进一步执行,这是 javascript 网页模型的一个怪癖。

    【讨论】:

    • 建议编辑答案“不值得”与“不值得”不同,我认为这是你的意图。只是为了避免读者混淆
    • >>异步编程是一种主要是单线程的编程范式
    • Lee,有两种主要的事件处理方式:阻塞,使用线程,或非阻塞/异步,使用事件循环。当然有混合方法,但我不明白为什么有必要在这里介绍它们。
    • 谢谢@Jerub,我有一个问题,在这种情况下,假设正在处理一个事件 [用户事件 onclick],而在此期间,另一个事件 [网络事件 - 服务器响应]发生;在这种情况下,是否会中断第一个事件而执行第二个事件?还是第一个事件处理完成,然后只处理第二个事件?
    【解决方案2】:

    我不了解 javascript,但例如在 Windows 窗体世界中,可以在没有多个线程的情况下进行异步调用。这与 Windows 消息泵的运行方式有关。基本上,Windows 窗体应用程序设置了一个消息队列,Windows 通过该消息队列放置消息,通知它有关事件的信息。例如,如果您移动鼠标,消息将被放置在该队列中。 Windows 窗体应用程序将处于无限循环中,消耗向其抛出的所有消息。根据每条消息包含的内容,它将移动窗口、重新绘制它们甚至调用用户定义的方法等等。对方法的调用由委托标识。当应用程序在队列中找到委托实例时,它会愉快地调用委托所引用的方法。

    因此,如果您在一个方法中做某事并希望在不创建新线程的情况下产生一些异步工作,您所要做的就是使用 Control.BeginInvoke 方法将委托实例放入队列中。现在,这实际上不是多线程的,但是如果您将非常小的工作投入到队列中,它看起来就像是多线程的。另一方面,如果你给它一个耗时的方法来执行,应用程序将冻结直到方法完成,这看起来就像一个卡住的应用程序,即使它正在做某事。

    【讨论】:

      【解决方案3】:

      不,但会涉及多个线程。

      异步调用可能会启动另一个线程来完成工作,或者它可能会将消息发布到另一个已经运行的线程上的队列中。调用者继续,被调用者在处理完消息后回调。

      如果您想在这种情况下进行同步调用,您需要发布一条消息并积极等待回调发生。

      所以总而言之:会涉及多个线程,但不一定会创建一个新线程。

      【讨论】:

        【解决方案4】:

        特别是关于 JavaScript 的一些注意事项:

        XMLHttpRequests 默认是非阻塞的。 send() 方法在请求被中继到底层网络堆栈后立即返回。来自服务器的响应将安排在事件循环上调用您的回调,正如其他优秀答案所讨论的那样。

        这不需要新线程。底层的socket API是可选的,类似于Java中的java.nio.channels

        可以通过将false 作为第三个参数传递给open() 来构造同步 XMLHttpRequest 对象。这将导致 send() 方法阻塞,直到从服务器接收到响应,从而使事件循环受网络延迟的支配,并可能挂起浏览器直到网络超时。这是一件坏事™。

        Firefox 3.5 将通过 Worker 类引入诚实的多线程 JavaScript。后台代码在完全独立的环境中运行,并通过在事件循环上调度回调与浏览器窗口进行通信。

        【讨论】:

        • 当你说A response from the server will schedule an invocation of your callback on the event loop时,哪个/哪个线程在事件循环上放置了一个事件以导致回调被调用?
        • @Yiling 该事件是由浏览器内部的 I/O 线程创建的,而不是执行 JavaScript 的线程。
        【解决方案5】:

        在许多 GUI 应用程序中,异步调用(如 Java 的 invokeLater)只是将 Runnable 对象添加到其 GUI 线程队列中。 GUI 线程已经创建,它不会创建新线程。但是异步系统甚至不需要线程。以 libevent 为例,它使用 select/poll/kqueue 等对套接字进行非阻塞调用,然后触发对代码的回调,完全没有线程。

        【讨论】:

          【解决方案6】:

          Javascript 模型是单线程。异步调用不是新线程,而是中断现有线程。这类似于内核中的中断。

          是的,使用单个线程进行异步调用是有意义的。思考方式如下:当您在单个线程中调用函数时,当前方法的状态会被压入堆栈(即局部变量)。子例程被调用并最终返回,此时原始状态从堆栈中弹出。

          使用异步回调,同样的事情也会发生!不同之处在于子程序由系统调用,而不是由当前代码调用子程序。

          【讨论】:

          • >>异步调用不是新线程
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-12
          • 1970-01-01
          • 1970-01-01
          • 2020-05-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多