答案 0 :(得分:13)
我认为实现标准异步编程模型的所有BeginXyz
操作都在线程池线程上运行回调,这使得应用程序自动成为多线程。
但是,您可以通过使用Control.Invoke
或更常见的SynchronizationContext
为Windows应用程序维护的单个GUI线程同步所有操作来实现单线程异步编程模型。
每次调用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)中的代码相同的线程上运行,因此您将仅使用线程池线程将回发转发回原始(单个)线程。 / p>
作为旁注,F#中的异步工作流更直接支持它,它可以用于非常优雅的GUI编程风格as described here。我不知道node.js
,但我想您可能也对F#异步工作流感到惊讶,因为它们非常适合基于异步/事件的/ ...编程风格: - )
答案 1 :(得分:7)
我正在研究.NET这样的宠物项目。我称之为ALE (Another Looping Event) ...因为啤酒。
现在非常 alpha,但它都是自制软件,因为和你一样,我想知道它是否可以完成。
与其他一些尝试不同,我看到它不仅仅是一个带有事件循环的Web服务器。您可以编写任何类型的基于事件循环的应用程序。
示例强>
以下代码将:
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.
有关该项目的更多信息将发布here。
答案 2 :(得分:6)
当然,它只需要一个事件循环。类似的东西:
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
,告诉它为什么要做某事。那时,你会发现你有一个message pump。
node.js
正在掩盖它在多线程的操作系统和应用程序上运行的事实。没有它,每次对网络或磁盘的调用都会阻塞。
答案 3 :(得分:3)
Reactive Extensions for .NET(Rx)专为异步和并行编程而设计。它允许您以反应式和交互式方式进行编程,无阻塞。您可以使用LINQ查询运算符,以及用于IObservable / IObserver接口的新运算符,它们是Rx的一部分。 Rx以IObservable / IObserver的形式提供IEnumerable / IEnumerator的数学对偶,这意味着您可以以声明方式使用所有LINQ标准查询运算符,而不是直接使用多线程API。
答案 4 :(得分:2)
不确定this是否正在寻找。 .NET可以在没有显式多线程的情况下进行异步回调。
答案 5 :(得分:2)
你的node.js示例并不适用,因为它运行的服务器正在执行所有必要的多线程。如果事件是基于相同的外部时钟信号执行的,那么它们不是异步的。您可以通过运行另一个应用程序来解决这个问题,这将在系统上创建两个进程。
在没有其他线程的情况下,您无法在单个系统上使单个进程运行相同的应用程序执行异步事件。
有关详细信息,请参阅此Asynchronous vs Multithreading question。
答案 6 :(得分:1)
即使您没有使用UI,也可以使用WPF调度程序。引用程序集WindowsBase
。然后,在您的启动代码中执行:
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(Initialize));
Dispatcher.Run();
从Initialize
和代码中的其他位置,使用Dispatcher.CurrentDispatcher.BeginInvoke
来安排后续异步方法的执行。
答案 7 :(得分:0)
我开发了一个基于HttpListener的服务器和一个事件循环,支持MVC,WebApi和路由。对于我所看到的,性能远远超过标准的IIS + MVC,对于MVCMusicStore,我将每秒100个请求和100%CPU从30%CPU转移到350。 如果有人试一试,我正在努力寻求反馈!
PS欢迎任何建议或更正!
答案 8 :(得分:0)
我相信它是可能的,这是一个用VB.NET和C#编写的开源示例:
https://github.com/perrybutler/dotnetsockets/
它使用Event-based Asynchronous Pattern (EAP),IAsyncResult Pattern和线程池(IOCP)。它会将消息(消息可以是任何本机对象,如类实例)序列化/编组为二进制数据包,通过TCP传输数据包,然后在接收端对数据包进行反序列化/解组,这样您就可以使用本机对象来处理。这部分有点像Protobuf或RPC。
它最初是作为一个&#34; netcode&#34;用于实时多人游戏,但它可以用于多种用途。不幸的是我从来没有使用它。也许别人会。
源代码有很多注释,因此应该很容易理解。享受!
编辑:经过压力测试和一些优化后,它能够在达到操作系统限制之前接受16,357个客户端。结果如下:
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
现在所有客户端都在localhost上运行,并在连接后向服务器发送一条小消息。 在另一个系统的后续测试中,服务器最多只有超过64,000个客户端连接(达到端口限制!),大约每秒2000个,消耗238 MB RAM。
答案 9 :(得分:0)
这是单线程EAP的my example。在不同的应用程序类型中有几种EAP实现:从简单的控制台应用程序到asp.net应用程序,tcp服务器等等。它们都建立在名为SingleSand的小框架上,理论上它可以插入任何类型的.net应用程序。
与之前的答案相反,我的实现充当了现有技术(asp.net,tcp套接字,RabbitMQ)与来自另一方的事件循环内的任务之间的中介。因此,目标不是制作纯粹的单线程应用程序,而是将事件循环集成到现有的应用程序技术中。我完全同意之前的帖子,即.net不支持纯粹的单线程应用程序。