我当时在看this question,想找一种方法来创建一个single-threaded,基于事件的非阻塞异步web服务器.网

This answer一开始看起来很有希望,因为它声称代码体在一个线程中运行.

然而,我在C#中测试了这一点:

using System;
using System.IO;
using System.Threading;

class Program
{
    static void Main()
    {
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

        var sc = new SynchronizationContext();
        SynchronizationContext.SetSynchronizationContext(sc);
        {
            var path = Environment.ExpandEnvironmentVariables(
                @"%SystemRoot%\Notepad.exe");
            var fs = new FileStream(path, FileMode.Open,
                FileAccess.Read, FileShare.ReadWrite, 1024 * 4, true);
            var bytes = new byte[1024];
            fs.BeginRead(bytes, 0, bytes.Length, ar =>
            {
                sc.Post(dummy =>
                {
                    var res = fs.EndRead(ar);

                    // Are we in the same thread?
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
                }, null);
            }, null);
        }
        Thread.Sleep(100);
    }
}

结果是:

1

所以看起来,与答案相反,启动读取的线程和结束读取的线程是相同的.

现在我的问题是,如何在中实现single-threaded个基于事件的非阻塞异步web服务器.网

推荐答案

整个SetSynchronizationContext是一个危险的问题,这只是一种编组机制,工作仍在IO线程池中进行.

您需要的是一种方法,可以排队并从主线程获取Asynchronous Procedure Calls个IO工作.许多更高级别的框架包装了这种功能,最著名的是libevent.

这里有一个关于各种选项的精彩回顾:Whats the difference between epoll, poll, threadpool?.

NET已经为您提供了一个特殊的"IO线程池",当您调用BeginXYZ方法时,它可以处理IO访问.此IO线程池中的每个处理器必须至少有一个线程.见:ThreadPool.SetMaxThreads.

如果单线程应用程序是一个关键需求(出于某些疯狂的原因),那么你当然可以在使用DllImport时互操作所有这些东西(参见example here)

然而,这将是一个非常complex and risky task:

我们为什么不支持APCs作为一种完成机制?对于用户代码来说,APC并不是一个好的通用完成机制.管理APCs引入的重入状态几乎是不可能的;例如,任何时候阻塞锁时,一些任意的I/O完成可能会接管线程.它可能会try 获取自己的锁,这可能会导致锁排序问题,从而导致死锁.要防止这种情况发生,需要精心设计,并确保在您可提醒的等待期间,其他人的代码永远不会运行,反之亦然.这大大限制了APC的实用性.

所以,概括一下.如果您想要一个single threaded管理的流程,它使用APC和完成端口完成所有工作,那么您必须手工编写代码.建造它既有风险,也很棘手.

如果你只是想要high scale联网,你可以继续使用BeginXYZ和family,并且放心,它会表现良好,因为它使用APC.你只需花很少的钱就可以在线程和.NET特定的实现.

起始:http://msdn.microsoft.com/en-us/magazine/cc300760.aspx

扩展服务器的下一步是使用异步I/O.异步I/O减轻了创建和管理线程的需要.这使得代码更加简单,同时也是一种更高效的I/O模型.异步I/O利用回调来处理传入的数据和连接,这意味着不需要设置和扫描列表,也不需要创建新的工作线程来处理挂起的I/O.

一个有趣的附带事实是,单线程并不是在Windows上使用完成端口实现异步套接字的最快方法请参见:http://doc.sch130.nsc.ru/www.sysinternals.com/ntw2k/info/comport.shtml

服务器的目标是通过让其线程避免不必要的阻塞,同时通过使用多个线程来最大化并行性,从而尽可能少地引发上下文切换.理想的情况是,每个处理器上都有一个线程主动为客户机请求提供服务,如果在完成请求时还有其他请求等待,这些线程就不会阻塞.然而,要使其正常工作,当一个处理客户机请求的线程阻塞I/O时(比如在处理过程中读取文件),应用程序必须有一种方法来激活另一个线程.

Node.js相关问答推荐

为什么请求正文未定义?

如何使用Express正确跟踪服务器应用程序上的所有传出的Node.js请求

赫斯基添加命令已弃用?

MongoDB的方面查询的Postgres类似功能

如何从谷歌云中部署的应用程序连接到mongoDB Atlas?

FHIR 服务器:尽管 JSON 格式正确,但在 POST 请求中接收未定义请求正文

如何模拟 mysql2 `getConnection`

如何获取mongoose中单个id数据的记录

Mongodb - 在数组数组中查找()

找不到模块bcryptjs

如何在没有 Typescript 的情况下以交互方式使用 Create-React-App?

在 React 和 Socket.io 中使用 Effect 钩子重新渲染两次

如何在 Nest.js 中使用查询参数?

来自 Node-aws 的 Dynamo Local:所有操作都失败无法对不存在的表执行操作

如何在 NodeJS 中获取操作系统用户名?

Nodejs续集批量更新

Passport 的 req.isAuthenticated 总是返回 false,即使我硬编码 done(null, true)

当我try 向我的 S3 存储桶 (Node.js) 发送内容时,AWS 缺少凭证

Heroku + Node:找不到模块错误

响应分块时获取整个响应正文?