【问题标题】:Asynchronous processing with a single thread单线程异步处理
【发布时间】:2014-02-04 18:54:59
【问题描述】:

即使在阅读http://krondo.com/?p=1209Does an asynchronous call always create/call a new thread? 之后,我仍然对如何在固有的单线程系统上提供异步调用感到困惑。我将解释我到目前为止的理解并指出我的疑问。

我读到的一个例子是描述一个 TCP 服务器提供异步处理请求 - 用户会调用一个方法,例如get(Callback c) 并且稍后会调用回调。现在,我在这里的第一个问题 - 我们已经有两个系统,一个服务器和一个客户端。这不是我的意思,因为事实上我们至少有两个线程——一个在服务器端,一个在客户端。

我读到的另一个例子是 JavaScript,因为这是 Node.js 的单线程异步系统最突出的例子。我无法理解的是,也许用 Java 术语思考的是:如果我执行下面的代码(为不正确的,可能是残暴的语法道歉):

function foo(){
    read_file(FIle location, Callback c) //asynchronous call, does not block
    //do many things more here, potentially for hours
}

读取文件的调用执行(某事)并返回,允许我的函数的其余部分执行。由于只有一个线程,即执行我的函数的线程,那么究竟同一个线程(也是唯一一个正在执行我的东西的线程)将如何从磁盘读取字节?

基本上,在我看来,我缺少一些类似于某种循环调度程序的底层机制,它本质上是单线程的,可能会将任务拆分为更小的任务或调用会产生的多线程组件一个线程并读取文件。

提前感谢所有 cmets 并在途中指出我的错误。

更新:感谢所有回复。帮助我解决此问题的其他良好资源如下:

  1. http://www.html5rocks.com/en/tutorials/async/deferred/
  2. http://lostechies.com/johnteague/2012/11/30/node-js-must-know-concepts-asynchrounous/
  3. http://www.interact-sw.co.uk/iangblog/2004/09/23/threadless (.NET)
  4. http://ejohn.org/blog/how-javascript-timers-work/(定时器的内部函数)
  5. http://www.mobl-lang.org/283/reducing-the-pain-synchronous-asynchronous-programming/

【问题讨论】:

  • 可能有也可能没有后台线程由挂起的操作隐式启动,但它完全不透明,与启动操作本身的 JavaScript 代码无关。 Web 浏览器(或 NodeJS、FTM)中 JavaScript 线程的核心事件循环是单线程的。

标签: java javascript multithreading asynchronous single-threaded


【解决方案1】:

真正的答案是,这取决于您所说的“单线程”。

多任务处理有两种方法:协作和中断驱动。合作,这是您引用的另一个 StackOverflow 项目所描述的,要求例程明确放弃处理器的所有权,以便它可以做其他事情。事件驱动系统通常以这种方式设计。优点是管理起来容易得多,并且避免了数据访问冲突的大部分风险,因为在任何时候都只会执行一段代码。缺点是,因为一次只做一件事,所有的事情要么被设计成相当快地执行,要么被分解成块(通过像yield()调用这样的显式暂停),或者系统将显示冻结,直到该事件已被完全处理。

另一种方法——线程或进程——主动让处理器远离运行代码块,在其他事情完成时暂停它们。这实现起来要复杂得多,并且在编码时需要更加小心,因为您现在有同时访问共享数据结构的风险,但功能更强大,而且——做得正确——更健壮和响应更快。

是的,这两种情况都确实涉及调度程序。在前一个版本中,调度程序只是旋转直到事件到达(从操作系统和/或运行时环境传递,这隐含地是另一个线程或进程)并在处理下一个到达之前调度该事件。

【讨论】:

  • 所以,在 Java 脚本中,我从磁盘加载文件的调用将是放置在待处理队列上的一个事件或一系列事件?但是既然这样,我的代码就不是真正的执行线程了,因为只能有一个线程,其实就是主循环线程拉队列的事件而已……所以,如果主线程是进行事件拉取,显然必须有另一个线程正在调度这些事件 iu.e.把他们放在那个队列里,对吧?
  • 我不是真正的 Javascript 用户,所以我无法具体回答。但是请记住,您背后有一个完整的操作系统,以及运行 Javascript 的浏览器或其他运行时环境。它们有自己的线程/进程与您的 Javascript 分时,并且您的 Javascript 中的异步操作可能会被移交给他们。
【解决方案2】:

我在 JavaScript 中的想法是有一个保存事件的队列。在旧的 Java 生产者/消费者的说法中,有一个消费者线程从这个队列中拉出东西并执行每个注册的函数来接收当前事件。诸如异步调用(AJAX 请求完成)、超时或鼠标事件等事件一旦发生就会被推送到队列中。单个“消费者”线程将它们从队列中拉出并定位任何感兴趣的函数然后执行它们,它无法到达下一个事件,直到它完成调用在当前事件上注册的所有函数。因此,如果您有一个永远不会完成的处理程序,则队列只会填满 - 它被称为“阻塞”。

系统有多个线程(它至少有一个生产者和一个消费者),因为某些东西会生成要进入队列的事件,但作为事件处理程序的作者,您需要注意事件是在单线程,如果你进入一个紧密的循环,你将锁定唯一的消费者线程,使系统无响应。

所以在你的例子中:

 function foo(){
    read_file(location, function(fileContents) {
        // called with the fileContents when file is read
    }     
    //do many things more here, potentially for hours
 }

如果您按照 cmets 的说明执行操作并可能执行数小时 - 即使文件已被读取,处理 fileContents 的回调也不会触发数小时。一旦你点击了 foo() 的最后一个 },消费者线程就完成了这个事件,并且可以处理下一个事件,它将使用文件内容执行注册的回调。

HTH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-10
    • 2017-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-04
    • 2021-02-26
    • 1970-01-01
    相关资源
    最近更新 更多