你问题的核心是:
NodeJS 将不得不像在其他多线程架构中一样面临传统的并发问题,例如,写入没有写保护的同一个对象?
答案是这种情况通常是不可能的,因为 node.js 进程不共享内存。进程 A 中的 ObjectA、ObjectB 和 ObjectC 与进程 B 中的 ObjectA、ObjectB 和 ObjectC 不同。并且由于每个进程都是单线程的,所以不会发生争用。这是您发现 node.js 没有附带信号量或互斥模块的主要原因。此外,node.js 没有附带线程模块
这也解释了为什么“没人在乎”。因为他们认为这不可能发生。
node.js 集群的问题是缓存之一。因为进程 A 中的 ObjectA 和进程 B 中的 ObjectA 是完全不同的对象,所以它们会有完全不同的数据。对此的传统解决方案当然不是在应用程序中存储动态状态,而是将它们存储在数据库中(或 memcache)。如果需要,也可以在代码中实现自己的缓存/数据同步方案。毕竟数据库集群就是这样工作的。
当然,node 作为一个用 C 编写的程序,可以很容易地用 C 扩展,并且 npm 上有一些模块可以实现线程、互斥锁和共享内存。如果您故意选择违背 node.js/javascript 的设计理念,那么您有责任确保不会出错。
补充答案:
基于给定对象 B 更新对象 A 的请求(未完成),另一个使用给定对象 C 再次更新对象 A 的请求(在第一次请求之前完成)...然后结果将基于对象 B 而不是 C ,因为第一个请求实际上在第二个请求之后完成。
这在真正的单线程应用程序中不会有问题,因为第二个总是在第一个请求之后执行......
首先,让我澄清你的一个误解。这对于real single-threaded application 来说不是问题。这是一个伪代码中的单线程应用程序:
function main () {
timeout = FOREVER
readFd = []
writeFd = []
databaseSock1 = socket(DATABASE_IP,DATABASE_PORT)
send(databaseSock1,UPDATE_OBJECT_B)
databaseSock2 = socket(DATABASE_IP,DATABASE_PORT)
send(databaseSock2,UPDATE_OPJECT_C)
push(readFd,databaseSock1)
push(readFd,databaseSock2)
while(1) {
event = select(readFD,writeFD,timeout)
if (event) {
for (i=0; i<length(readFD); i++) {
if (readable(readFD[i]) {
data = read(readFD[i])
if (data == OBJECT_B_UPDATED) {
update(objectA,objectB)
}
if (data == OBJECT_C_UPDATED) {
update(objectA,objectC)
}
}
}
}
}
}
如您所见,上面的程序中没有线程,只是使用select 系统调用的异步I/O。上面的程序可以很容易地直接翻译成单线程的 C 或 Java 等(实际上,类似的东西是 javascript 事件循环的核心)。
但是,如果对 UPDATE_OBJECT_C 的响应在对 UPDATE_OBJECT_B 的响应之前到达,则最终状态将是 objectA 根据 objectB 的值而不是 objectC 的值进行更新。
任何语言的异步单线程程序都无法避免这种情况,node.js 也不例外。
但是请注意,您最终不会处于损坏状态(尽管您最终会处于意外状态)。多线程程序的情况更糟,因为没有锁/信号量/互斥锁,对update(objectA,objectB) 的调用可能会被对update(objectA,objectC) 的调用中断,并且objectA 将被破坏。这是您在单线程应用程序中不必担心的问题,在 node.js 中您也不必担心。
如果您需要严格的时间顺序更新,您仍然需要等待第一次更新完成,将第一次更新标记为无效或为第二次更新生成错误。通常对于网络应用程序(如 stackoverflow)会返回错误(例如,如果您尝试提交评论而其他人已经更新了 cmets)。