有人能给我解释/指引一下,Node.js的异步模型(非阻塞线程)与其他语言例如C#的异步处理I/O的方式有什么区别吗?在我看来,这两种模型都是相同的。请给予建议。
有人能给我解释/指引一下,Node.js的异步模型(非阻塞线程)与其他语言例如C#的异步处理I/O的方式有什么区别吗?在我看来,这两种模型都是相同的。请给予建议。
这两个模型非常相似。有两个主要区别,其中一个(根据“即将”定义的说法)即将消失。
一个差异是Node.js是异步单线程的,而ASP.NET是异步多线程的。这意味着Node.js代码可以做出一些简化的假设,因为你的所有代码始终运行在同一个确切的线程上。所以当你的ASP.NET代码等待时,它可能会在不同的线程上恢复,并且你需要避免诸如线程本地状态之类的事情。
然而,对于ASP.NET来说,这种差异也是一种优势,因为它意味着async
ASP.NET可以直接扩展到服务器的全部功能。如果你考虑一个8核的机器,那么ASP.NET可以同时处理(同步部分的)8个请求。如果你把Node.js放在一个升级过的服务器上,通常实际上会运行8个单独的Node.js实例,并添加类似nginx或一个简单的自定义负载均衡器来处理该服务器的路由请求。这也意味着如果你想要其他共享服务器资源(例如缓存),那么你还需要将它们移出进程。
另一个主要区别实际上是语言上的区别,而不是平台上的区别。JavaScript的异步支持仅限于回调和承诺,即使你使用最好的库,在处理任何非平凡的情况时,你仍然会得到非常尴尬的代码。相比之下,C# / VB中async
/await
的支持允许你编写非常自然的异步代码(更重要的是,可维护的异步代码)。
async
/await
一样自然。如果想要立即使用这个“即将到来”的功能,可以使用 V8 3.19 中添加的生成器,在 Node.js 0.11.2(不稳定分支)中进行了整合。使用 --harmony
或 --harmony-generators
来明确启用生成器支持。await
可以确保方法恢复到同一“请求上下文”,而不是同一“线程”。 - Stephen Clearyasync
),我认为Node.js将会更容易编写。谁知道呢,也许最终因此而胜出。JS正在进行一些很棒(也是非常需要的)改进,包括async
。 - Stephen Cleary编辑: 由于Node.js V8 JavaScript引擎支持生成器,在ECMAScript 6草案中定义, JavaScript代码中的“回调地狱”也可以轻松避免。 它为JavaScript带来了某种形式的异步/等待
网络上有很多地方描述了Node的架构,但这里有一些内容:http://johanndutoit.net/presentations/2013/02/gdg-capetown-nodejs-workshop-23-feb-2013/index.html#1
while(true);
会导致Nodejs挂起,因为在这种情况下,当前堆栈没有退出,下一个循环永远不会启动。ThreadPool.QueueUserWorkItem((o)=>{eventSource.Fire();});
eventSource.Fired += ()=>{
// the following line might never execute, because a parallel execution stack in a thread pool could have already been finished by the time the callback added.
Console.WriteLine("1");
}
这里有一个Hello World .NET代码,类似于Nodejs,演示了在单个线程上进行异步处理和使用线程池进行异步IO。(.NET包括TPL和IAsyncResult版本的异步IO操作,但是在本例中没有区别。无论如何,所有操作最终都会在线程池中以不同的线程完成。)
void Main()
{
// Initializing the test
var filePath = Path.GetTempFileName();
var filePath2 = Path.GetTempFileName();
File.WriteAllText(filePath, "World");
File.WriteAllText(filePath2, "Antipodes");
// Simulate nodejs
var loop = new Loop();
// Initial method code, similar to server.js in Nodejs.
var fs = new FileSystem();
fs.ReadTextFile(loop, filePath, contents=>{
fs.WriteTextFile(loop, filePath, string.Format("Hello, {0}!", contents),
()=>fs.ReadTextFile(loop,filePath,Console.WriteLine));
});
fs.ReadTextFile(loop, filePath2, contents=>{
fs.WriteTextFile(loop, filePath2, string.Format("Hello, {0}!", contents),
()=>fs.ReadTextFile(loop,filePath2,Console.WriteLine));
});
// The first javascript-ish cycle have finished.
// End of a-la nodejs code, but execution have just started.
// First IO operations could have finished already, but not processed by callbacks yet
// Process callbacks
loop.Process();
// Cleanup test
File.Delete(filePath);
File.Delete(filePath2);
}
public class FileSystem
{
public void ReadTextFile(Loop loop, string fileName, Action<string> callback)
{
loop.RegisterOperation();
// simulate async operation with a blocking call on another thread for demo purposes only.
ThreadPool.QueueUserWorkItem(o=>{
Thread.Sleep(new Random().Next(1,100)); // simulate long read time
var contents = File.ReadAllText(fileName);
loop.MakeCallback(()=>{callback(contents);});
});
}
public void WriteTextFile(Loop loop, string fileName, string contents, Action callback)
{
loop.RegisterOperation();
// simulate async operation with a blocking call on another thread for demo purposes only.
ThreadPool.QueueUserWorkItem(o=>{
Thread.Sleep(new Random().Next(1,100)); // simulate long write time
File.WriteAllText(fileName, contents);
loop.MakeCallback(()=>{callback();});
});
}
}
public class Loop
{
public void RegisterOperation()
{
Interlocked.Increment(ref Count);
}
public void MakeCallback(Action clientAction)
{
lock(sync)
{
ActionQueue.Enqueue(()=>{clientAction(); Interlocked.Decrement(ref Count);});
}
}
public void Process()
{
while(Count > 0)
{
Action action = null;
lock(sync)
{
if(ActionQueue.Count > 0)
{
action = ActionQueue.Dequeue();
}
}
if( action!= null )
{
action();
}
else
{
Thread.Sleep(10); // simple way to relax a little bit.
}
}
}
private object sync = new object();
private Int32 Count;
private Queue<Action> ActionQueue = new Queue<Action>();
}
await
帮助你 消费 异步操作,而不是编写大量的回调函数。 - SLaks