在.NET中使用C#可以在没有multithreading的情况下实现基于事件的asynchronous模式吗?

我为Node.js的build筑devise感到惊讶,并想知道C#是否有这样的devise能力:

asynchronous,基于事件/事件循环,无阻塞I / O,无需multithreading。

我认为实现标准asynchronous编程模型的所有BeginXyz操作都在线程池线程上运行callback,这使应用程序自动进行multithreading化。

但是,您可以通过使用Control.Invoke或更一般的SynchronizationContext通过为Windows应用程序维护的单个GUI线程同步所有操作来实现单线程asynchronous编程模型。

每次调用BeginXyz将不得不按照以下这些行重写:

 // Start asynchronous operation here (1) var originalContext = SynchronizationContext.Current; obj.BeginFoo(ar => // Switch to the original thread originalContext.Post(ignored => { var res = obj.EndFoo(); // Continue here (2) })); 

标记为(2)的代码将继续在(1)中的代码所在的同一线程上运行,因此您将仅使用线程池线程将回发转发回原始(单个)线程。

另外,F#中的asynchronous工作stream可以更直接地支持这一点,并且可以使用这里所描述的非常优雅的GUI编程风格。 我不知道node.js ,但我想你也可能会惊讶于F# asynchronous工作stream程,因为它们对于基于asynchronous/事件的/ …编程风格非常酷:-)

作为一个宠物项目,我正在使用.NET来处理这样的事情。 我把它称为ALE(另一个循环事件) …因为啤酒。

现在是非常的阿尔法,但都是自制的,因为像你一样,我想知道是否可以做到。

  • 这是一个事件循环体系结构。
  • 它利用.NET的非阻塞asynchronousI / O调用
  • 它使用callback式调用来帮助开发人员编写更具可读性的asynchronous代码。
  • asynchronousWeb套接字实现
  • asynchronoushttp服务器实现
  • asynchronoussql客户端实现

不像其他一些尝试在这个我已经看到它不只是一个事件循环的Web服务器。 你可以写任何types的基于事件循环的应用程序。


以下代码将会:

  • 启动事件循环
  • 在端口1337上启动Web服务器
  • 在端口1338上启动Web套接字服务器
  • 然后阅读一个文件和“DoSomething”:
 EventLoop.Start(() => { //create a web server Server.Create((req, res) => { res.Write("<h1>Hello World</h1>"); }).Listen("http://*:1337"); //start a web socket server Net.CreateServer((socket) => { socket.Receive((text) => { socket.Send("Echo: " + text); }); }).Listen("127.0.0.1", 1338, "http://origin.com"); //Read a file File.ReadAllText(@"C:\Foo.txt", (text) => { DoSomething(text); }); }); 

所以我想我的答案是“是”,它可以在C#中完成…或几乎任何语言的事情。 真正的技巧是能够利用本地非阻塞I / O。

有关该项目的更多信息将在这里发布。

当然,这只需要一个事件循环。 就像是:

 class EventLoop { List<Action> MyThingsToDo { get; set; } public void WillYouDo(Action thing) { this.MyThingsToDo.Add(thing); } public void Start(Action yourThing) { while (true) { Do(yourThing); foreach (var myThing in this.MyThingsToDo) { Do(myThing); } this.MyThingsToDo.Clear(); } } void Do(Action thing) { thing(); } } class Program { static readonly EventLoop e = new EventLoop(); static void Main() { e.Start(DoSomething); } static int i = 0; static void DoSomething() { Console.WriteLine("Doing something..."); e.WillYouDo(() => { results += (i++).ToString(); }); Console.WriteLine(results); } static string results = "!"; } 

很快,你会想摆脱DoSomething并要求所有的工作与MyThingsToDo注册。 然后,你会想要通过一个enum或东西给每个ThingToDo ,告诉它为什么它正在做的事情。 在这一点上,你会意识到你有一个消息泵 。

顺便说一句,我会说node.js是光彩的事实,它运行在multithreading的操作系统和应用程序。 没有这一点,每个电话networking或磁盘将阻止。

.NET的反应式扩展 (Rx)专为asynchronous和并行编程而devise。 它允许您以反应式与交互式方式进行编程,无阻塞。 您使用LINQ查询运算符,并为IOxservable / IObserver接口(它们是Rx的一部分)添加新的运算符。 Rx以IObservable / IObserver的forms提供了IEnumerable / IEnumerator的math对偶,这意味着您可以以声明的方式使用所有的LINQ标准查询操作符,而不是直接使用multithreadingAPI。

不知道这是你在找什么。 .NET可以在没有显式multithreading的情况下执行asynchronouscallback。

你的node.js的例子并不真正适用,因为它运行的服务器正在执行所有必要的multithreading。 如果事件是基于相同的外部时钟信号执行的,那么它们不是asynchronous的。 您可以通过运行另一个应用程序来解决这个问题,并在系统上创build两个进程。

没有办法让一个系统上的同一个应用程序作为单个进程运行,而不需要另一个线程来执行asynchronous事件。

有关更多详细信息,请参阅此asynchronous与multithreading问题 。

即使您不在UI中,也可以使用WPF调度程序。 引用程序集WindowsBase 。 然后,在您的启动代码执行:

 Dispatcher.CurrentDispatcher.BeginInvoke(new Action(Initialize)); Dispatcher.Run(); 

Initialize和其他代码中,使用Dispatcher.CurrentDispatcher.BeginInvoke来调度后续asynchronous方法的执行。

我开发了一个基于HttpListener和事件循环的服务器,支持MVC,WebApi和路由。 对于我所看到的性能比标准的IIS + MVC要好得多,对于MVCMusicStore,我从每秒100个请求和100%的CPU移动到了30%的CPU。 如果有人愿意尝试,我正在努力反馈!

PS任何build议或更正是值得欢迎的!

  • 文档
  • Node.Cs上的MvcMusicStore示例端口
  • 在Nuget上的软件包

我相信这是可能的,这是一个用VB.NET和C#编写的开源示例:

https://github.com/perrybutler/dotnetsockets/

它使用基于事件的asynchronous模式(EAP) , IAsyncResult模式和线程池( IOCP )。 它会将消息序列化/封送(消息可以是任何本地对象,如类实例)为二进制数据包,通过TCP传输数据包,然后在接收端对数据包进行反序列化/解组,以便让本地对象与。 这部分有点像Protobuf或RPC。

它最初是作为实时多人游戏的“networking编码”开发的,但它可以用于很多目的。 不幸的是我从来没有使用它。 也许别人会。

源代码有很多评论,所以应该很容易遵循。 请享用!

编辑:经过压力testing和一些优化,它能够接受16357客户端之前达到操作系统的限制。 结果如下:

 Simulating 1000 client connections over 16 iterations... 1) 485.0278 ms 2) 452.0259 ms 3) 495.0283 ms 4) 476.0272 ms 5) 472.027 ms 6) 477.0273 ms 7) 522.0299 ms 8) 516.0295 ms 9) 457.0261 ms 10) 506.029 ms 11) 474.0271 ms 12) 496.0283 ms 13) 545.0312 ms 14) 516.0295 ms 15) 517.0296 ms 16) 540.0309 ms All iterations complete. Total duration: 7949.4547 ms 

现在,所有客户端都运行在本地主机上,并在连接之后向服务器发送一条小消息。 在不同系统的后续testing中,服务器以每秒2000次的速度超过了64,000个客户端连接(达到端口限制!),耗用了238 MB的RAM。

这是我的单线程EAP的例子 。 在不同的应用程序types中有几种EAP的实现:从简单的控制台应用程序到asp.net应用程序,tcp服务器等等。 它们都build立在名为SingleSand的小型框架上,理论上它可以插入任何types的.net应用程序。

与之前的回答相比,我的实现充当了一方的现有技术(asp.net,tcp套接字,RabbitMQ)与另一方的事件循环内的任务之间的中介。 因此,我们的目标不是创build一个纯粹的单线程应用程序,而是将事件循环集成到现有的应用程序技术中。 我完全同意以前的post.net不支持纯单线程应用程序。