Javascript/Node 中从不执行用户代码的隐藏线程:是否可能,如果可能,是否会导致竞争条件的神秘可能性?
Hidden threads in Javascript/Node that never execute user code: is it possible, and if so could it lead to an arcane possibility for a race condition?
请参阅问题底部的更新,基于comments/answers:这个问题实际上是关于 隐藏 线程不执行的可能性回调。
我对涉及 Node Request module 的潜在神秘场景有疑问,其中:
一个完整的 HTTP 请求在网络上构建并执行(花费多少毫秒甚至几秒)
... before 单个函数在运行时 在本地机器上执行(通常以纳秒为单位) ?) - 详情见下文
我发布这篇文章主要是为了 完整性检查 只是为了确保我没有误解 Node / JS / Request 模块代码。
来自 examples in the Request module(参见该部分的第二个示例),是这样的:
// Copied-and-pasted from the second example in the
// Node Request library documentation, here:
// https://www.npmjs.com/package/request#examples
// ... My ARCANE SCENARIO is injected in the middle
var request = require('request')
request(
{ method: 'GET'
, uri: 'http://www.google.com'
, gzip: true
}
, function (error, response, body) {
// body is the decompressed response body
console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity'))
console.log('the decoded data is: ' + body)
}
)
// **************************************************** //
// Is the following scenario possible?
//
// <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
//
// Let us pretend that the current thread HANGS here,
// but that the request had time to be sent,
// and the response is pending being received by the thread
//
// <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
// **************************************************** //
.on('data', function(data) {
// decompressed data as it is received
console.log('decoded chunk: ' + data)
})
.on('response', function(response) {
// unmodified http.IncomingMessage object
response.on('data', function(data) {
// compressed data as it is received
console.log('received ' + data.length + ' bytes of compressed data')
})
})
我在代码片段中指出了我的神秘场景。
假设 Node 进程在指示的点处挂起,但该 Node 内部(在 隐藏 线程中,对 Javascript 不可见,因此不调用 any 回调)能够构建请求,并通过网络发送;假设挂起一直持续到接收到响应(例如,分为两个块)并等待 Node.js 处理。 (这种情况肯定是神秘的,我什至不确定理论上是否可能。)
然后假设hang结束,上面的Node线程被唤醒。此外,假设(以某种方式)Node 能够处理响应一直到执行上面代码中的回调函数的点(但没有移动到原始代码路径中代码中的 'hanged' 点 - - 再一次,如果这在理论上是可能的)。
上述神秘场景在理论上是否可能?如果是这样,在对象上安排 'data'
事件之前,数据包是否会通过网络接收并合并,准备传递给回调函数?在这种情况下,如果可能的话,我想 'data'
事件会被错过。
同样,我知道这是一个神秘的场景 - 考虑到所涉及的内部机制和编码,也许这在理论上是不可能的。
这是我的问题 - 上述神秘的场景,其极不可能的竞争条件,在理论上是否可行?
我问只是为了确保我没有遗漏一些关键点。谢谢。
更新:来自评论和答案:我现在已经澄清了我的问题。 'arcane scenario' 需要有一个隐藏线程(因此不能执行任何用户代码,包括回调)构造请求,通过网络发送它,并接收响应 - 没有任何回调触发,包括'data'
回调 - 并在 'response'
回调准备好调用时停止,等待(单个)可见 JS 线程唤醒。
Node.js只涉及一个线程;事件循环用于处理 运行 异步的任务,任何排队的任务都不会中断任何已经 运行 的任务。所以不,那里没有竞争条件。
nodejs Javsacript 执行是单线程和事件驱动的。这意味着一切都通过事件队列运行。 Javascript 执行的线程一直运行到完成,然后系统检查事件队列以查看是否还有其他事情要做(等待触发的计时器、等待调用的异步回调等)。
nodejs 在它的某些实现中确实使用了一些内部线程(例如文件I/O),但据我了解它在网络中不使用线程。但是,是否有一些内部线程并不重要,因为网络等子系统与主 nodejs JS 线程之间的所有通信都是通过事件队列进行的。
一个 nodejs 执行线程永远不会被打断去做别的事情。它完成并运行完成,然后 JS 引擎检查事件队列以查看是否还有其他等待执行的内容。
当套接字上有可用的传入数据时,事件将放入事件队列中。当前正在执行的 nodejs Javascript 完成了它正在做的事情,然后 JS 引擎看到事件队列中有一个事件并触发该事件。如果有与该事件关联的函数回调或事件处理程序(通常有),则会调用它来执行该事件。
如果某些基础设施(例如网络)的内部发生故障,那么 nodejs 代码所发生的一切就是某些网络事件没有发生。 nodejs 代码有它的事件处理程序,只是在基础设施松开并创建事件之前不接收他们正在等待的事件。这不会在 nodejs 代码中造成任何类型的挂起。
因此,在您的更新中:
From comments & answers: I now have clarified my question. The 'arcane
scenario' would require that there is a HIDDEN thread (which therefore
CANNOT execute any USER code, including CALLBACKS) that constructs the
request, sends it over the network, and receives the response -
WITHOUT having any callbacks to trigger, including the 'data' callback
- and stops short just at the point that the 'response' callback is ready to be called, waiting for the (single) visible JS thread to wake
up.
nodejs 线程运行完成,然后 JS 引擎等待新事件发生(例如放入事件队列)。当该事件发生时,JS 引擎运行与该事件对应的代码(事件处理程序、回调等)。你让它听起来像是单个可见的 JS 线程正在等待唤醒,它可能会卡在那里,因为其他一些子系统被挂起。事实并非如此。唯一可能发生的事情是,单个 JS 线程具有事件处理程序的某些事件永远不会发生。这与您向服务器发送消息并且您有一个事件处理程序来查看响应但服务器从不发送响应的情况没有什么不同。您的 nodejs 代码继续处理其他事件(计时器、其他网络、其他 I/O),但此特定事件从未发生,因为其他服务器从未发送过会触发该事件的数据。没有挂起。
这就是 "evented I/O",nodejs 是这样描述自己的。
不,这不可能发生。
是的,确实有 "hidden" 后台线程执行异步方法的工作,但 那些不调用回调 。 javascript 的所有执行确实发生在同一个线程上,同步,顺序。 data
事件回调将始终 异步执行 ,即在当前 script/function 运行 完成后执行。
虽然在创建回调并将其附加到事件发射器之前确实已经有来自网络的数据包到达,但在发送请求之前总是创建在最低级别上侦听数据包的回调 - 这是一个参数到本机 "makeRequest" 方法,并且可以从一开始就调用。因此,当数据包在当前脚本(仍然被构建事件发射器和附加处理程序占用)完成之前到达时,这个 事件被排队 ,回调只会在事件循环准备就绪——在下一轮。至此,data
事件回调当然创建并附加。
请参阅问题底部的更新,基于comments/answers:这个问题实际上是关于 隐藏 线程不执行的可能性回调。
我对涉及 Node Request module 的潜在神秘场景有疑问,其中:
一个完整的 HTTP 请求在网络上构建并执行(花费多少毫秒甚至几秒)
... before 单个函数在运行时 在本地机器上执行(通常以纳秒为单位) ?) - 详情见下文
我发布这篇文章主要是为了 完整性检查 只是为了确保我没有误解 Node / JS / Request 模块代码。
来自 examples in the Request module(参见该部分的第二个示例),是这样的:
// Copied-and-pasted from the second example in the
// Node Request library documentation, here:
// https://www.npmjs.com/package/request#examples
// ... My ARCANE SCENARIO is injected in the middle
var request = require('request')
request(
{ method: 'GET'
, uri: 'http://www.google.com'
, gzip: true
}
, function (error, response, body) {
// body is the decompressed response body
console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity'))
console.log('the decoded data is: ' + body)
}
)
// **************************************************** //
// Is the following scenario possible?
//
// <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
//
// Let us pretend that the current thread HANGS here,
// but that the request had time to be sent,
// and the response is pending being received by the thread
//
// <-- HANG HANG HANG HANG HANG HANG HANG HANG HANG -->
// **************************************************** //
.on('data', function(data) {
// decompressed data as it is received
console.log('decoded chunk: ' + data)
})
.on('response', function(response) {
// unmodified http.IncomingMessage object
response.on('data', function(data) {
// compressed data as it is received
console.log('received ' + data.length + ' bytes of compressed data')
})
})
我在代码片段中指出了我的神秘场景。
假设 Node 进程在指示的点处挂起,但该 Node 内部(在 隐藏 线程中,对 Javascript 不可见,因此不调用 any 回调)能够构建请求,并通过网络发送;假设挂起一直持续到接收到响应(例如,分为两个块)并等待 Node.js 处理。 (这种情况肯定是神秘的,我什至不确定理论上是否可能。)
然后假设hang结束,上面的Node线程被唤醒。此外,假设(以某种方式)Node 能够处理响应一直到执行上面代码中的回调函数的点(但没有移动到原始代码路径中代码中的 'hanged' 点 - - 再一次,如果这在理论上是可能的)。
上述神秘场景在理论上是否可能?如果是这样,在对象上安排 'data'
事件之前,数据包是否会通过网络接收并合并,准备传递给回调函数?在这种情况下,如果可能的话,我想 'data'
事件会被错过。
同样,我知道这是一个神秘的场景 - 考虑到所涉及的内部机制和编码,也许这在理论上是不可能的。
这是我的问题 - 上述神秘的场景,其极不可能的竞争条件,在理论上是否可行?
我问只是为了确保我没有遗漏一些关键点。谢谢。
更新:来自评论和答案:我现在已经澄清了我的问题。 'arcane scenario' 需要有一个隐藏线程(因此不能执行任何用户代码,包括回调)构造请求,通过网络发送它,并接收响应 - 没有任何回调触发,包括'data'
回调 - 并在 'response'
回调准备好调用时停止,等待(单个)可见 JS 线程唤醒。
Node.js只涉及一个线程;事件循环用于处理 运行 异步的任务,任何排队的任务都不会中断任何已经 运行 的任务。所以不,那里没有竞争条件。
nodejs Javsacript 执行是单线程和事件驱动的。这意味着一切都通过事件队列运行。 Javascript 执行的线程一直运行到完成,然后系统检查事件队列以查看是否还有其他事情要做(等待触发的计时器、等待调用的异步回调等)。
nodejs 在它的某些实现中确实使用了一些内部线程(例如文件I/O),但据我了解它在网络中不使用线程。但是,是否有一些内部线程并不重要,因为网络等子系统与主 nodejs JS 线程之间的所有通信都是通过事件队列进行的。
一个 nodejs 执行线程永远不会被打断去做别的事情。它完成并运行完成,然后 JS 引擎检查事件队列以查看是否还有其他等待执行的内容。
当套接字上有可用的传入数据时,事件将放入事件队列中。当前正在执行的 nodejs Javascript 完成了它正在做的事情,然后 JS 引擎看到事件队列中有一个事件并触发该事件。如果有与该事件关联的函数回调或事件处理程序(通常有),则会调用它来执行该事件。
如果某些基础设施(例如网络)的内部发生故障,那么 nodejs 代码所发生的一切就是某些网络事件没有发生。 nodejs 代码有它的事件处理程序,只是在基础设施松开并创建事件之前不接收他们正在等待的事件。这不会在 nodejs 代码中造成任何类型的挂起。
因此,在您的更新中:
From comments & answers: I now have clarified my question. The 'arcane scenario' would require that there is a HIDDEN thread (which therefore CANNOT execute any USER code, including CALLBACKS) that constructs the request, sends it over the network, and receives the response - WITHOUT having any callbacks to trigger, including the 'data' callback - and stops short just at the point that the 'response' callback is ready to be called, waiting for the (single) visible JS thread to wake up.
nodejs 线程运行完成,然后 JS 引擎等待新事件发生(例如放入事件队列)。当该事件发生时,JS 引擎运行与该事件对应的代码(事件处理程序、回调等)。你让它听起来像是单个可见的 JS 线程正在等待唤醒,它可能会卡在那里,因为其他一些子系统被挂起。事实并非如此。唯一可能发生的事情是,单个 JS 线程具有事件处理程序的某些事件永远不会发生。这与您向服务器发送消息并且您有一个事件处理程序来查看响应但服务器从不发送响应的情况没有什么不同。您的 nodejs 代码继续处理其他事件(计时器、其他网络、其他 I/O),但此特定事件从未发生,因为其他服务器从未发送过会触发该事件的数据。没有挂起。
这就是 "evented I/O",nodejs 是这样描述自己的。
不,这不可能发生。
是的,确实有 "hidden" 后台线程执行异步方法的工作,但 那些不调用回调 。 javascript 的所有执行确实发生在同一个线程上,同步,顺序。 data
事件回调将始终 异步执行 ,即在当前 script/function 运行 完成后执行。
虽然在创建回调并将其附加到事件发射器之前确实已经有来自网络的数据包到达,但在发送请求之前总是创建在最低级别上侦听数据包的回调 - 这是一个参数到本机 "makeRequest" 方法,并且可以从一开始就调用。因此,当数据包在当前脚本(仍然被构建事件发射器和附加处理程序占用)完成之前到达时,这个 事件被排队 ,回调只会在事件循环准备就绪——在下一轮。至此,data
事件回调当然创建并附加。