即使在阅读http://krondo.com/?p=1209或Does 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
}
对read文件的调用执行(sth)并返回,允许我的其余函数执行。既然只有一个线程,即执行我的函数的线程,那么同一个线程(执行我的东西的唯一一个线程)将如何从磁盘读取字节?
基本上,在我看来,我缺少一些基本机制,就像某种类型的循环调度程序一样,本质上是单线程的,可能会将任务拆分为较小的或调用多个组成的组件,这些组件会产生一个线程并在中读取文件。
提前感谢所有评论并指出我在路上的错误。
更新:感谢所有回复。更多有助于我解决这个问题的好消息来源:
答案 0 :(得分:2)
真正的答案是,它取决于“单线程”的含义。
多任务处理有两种方法:协作和中断驱动。 Cooperative,这是你引用的其他StackOverflow项目所要求的,它要求例程明确放弃处理器的所有权,以便它可以做其他事情。事件驱动系统通常以这种方式设计。优点是管理起来要容易得多,并且避免了大部分冲突访问数据的风险,因为任何时候只有一大块代码在执行。缺点是,因为一次只做一件事,所有事情都必须设计得相当快,或者分解成块(通过像yield()
调用这样的显式暂停),或者系统将冻结,直到该事件完全处理完毕。
另一种方法 - 线程或进程 - 主动使处理器远离运行代码块,在完成其他操作时暂停它们。这实现起来要复杂得多,并且需要更加谨慎的编码,因为您现在可能同时访问共享数据结构,但功能更强大 - 做得更好 - 更强大,更灵敏。
是的,在任何一种情况下都确实涉及调度程序。在以前的版本中,调度程序只是旋转直到事件到达(从操作系统和/或运行时环境传递,这是隐式的另一个线程或进程)并在处理下一个到达之前调度该事件。
答案 1 :(得分:0)
我在JavaScript中的想法是有一个包含事件的Queue。在旧的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
}
如果您按照您的意见说并执行可能持续数小时 - 即使文件已被读取,处理fileContents的回调也不会激活数小时。一旦你点击了foo()的最后一个消息线程就完成了这个事件,并且可以处理下一个事件,它将用文件内容执行注册的回调。
HTH