Is it possible in .NET, using C#, to achieve event based asynchronous pattern without multithreading?
I am amazed by the architectural design of Node.js and was wondering if C# is capable of such a design:
Asynchronous, event based / event loop, non-blocking I/O without multithrea开发者_如何学运维ding.
I think that all the BeginXyz
operations that implement the standard asynchronous programming model run the callback on a thread pool thread, which makes the application automatically multi-threaded.
However, you can achieve single-threaded asynchronous programming model by synchronizing all the operations through the single GUI thread that is maintained for windows applications using Control.Invoke
or more generally, SynchronizationContext
.
Each call to BeginXyz
would have to be rewritten along these lines:
// 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)
}));
The code marked as (2) will continue running on the same thread as the code in (1), so you'll use the thread-pool thread only for forwarding the postback back to the original (single) thread.
As a side-note, this is more directly supported by asynchronous workflows in F# and it can be used for quite elegant style of GUI programming as described here. I don't know node.js
, but I suppose that you may be also amazed by F# asynchronous workflows as they are really cool for asynchronous/event based/... style of programming :-)
I'm working on just such a thing in .NET as a pet project. I call it ALE (Another Looping Event)... because beer.
It's extremely alpha right now, but it's all homebrew because, like you, I wanted to know if it can be done.
- It's an event loop architecture.
- It leverages .NET's non-blocking async I/O calls
- It uses callback-style calls to assist developers writing async code that is a little more readable.
- Async web sockets implementation
- Async http server implementation
- Async sql client implemenation
Unlike some of the other tries at this I've seen it's not just a web server with an event loop. You could write any sort of event loop based application off of this.
Example
The following code will:
- Start the Event Loop
- Start a web server on port 1337
- Start a web socket server on port 1338
- Then read a file and "DoSomething" with it:
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);
});
});
So I guess my answer is "Yes", it can be done in C#... or almost any language for that matter. The real trick is being able to leverage native non-blocking I/O.
More information about the project will be posted here.
Sure, it just requires an event loop. Something like:
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 = "!";
}
Pretty soon, you'll want to get rid of DoSomething
and require all work to be registered with MyThingsToDo
. Then, you'll want to pass an enum
or something to each ThingToDo
that tells it why it's doing something. At that point, you'll realize you have a message pump.
BTW, I'd say node.js
is glossing over the fact that it's running on an OS and application that's multithreaded. Without that, each call to the network or disk would block.
The Reactive Extensions for .NET (Rx) is designed for asynchronous and parallel programming. It allows you to program in a reactive vs. interactive way, non-blocking. You use the LINQ query operators, and new ones for IObservable/IObserver interfaces, which are part of Rx. Rx provides the mathematical dual of IEnumerable/IEnumerator, in the form of IObservable/IObserver, which means you can use all of the LINQ standard query operators, in a declarative way, as opposed to using the multithreading APIs directly.
Not sure if this is what you're looking for. .NET can do asynchronous callbacks without explicit multi threading.
your example of node.js is not really applicable since the server that it's running on is performing all the necessary multithreading. If the events are executed based on the same external clock signal, then they're not asynchronous. You can get around this by running another application and that would create two processes on the system.
There is no way that you can make the same application running as a single process on a single system perform asynchronous events without having another thread.
For more details see this Asynchronous vs Multithreading question.
You can use the WPF dispatcher, even if you aren't doing UI. Reference the assembly WindowsBase
. Then, in your startup code execute:
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(Initialize));
Dispatcher.Run();
From Initialize
and elsewhere in your code, use Dispatcher.CurrentDispatcher.BeginInvoke
to schedule execution of subsequent async methods.
i developed a server based on HttpListener and an event loop, supporting MVC, WebApi and routing. For what i have seen the performances are far better than standard IIS+MVC, for the MVCMusicStore i moved from 100 requests per seconds and 100% CPU to 350 with 30% CPU. If anybody would give it a try i am struggling for feedbacks!
PS any suggestion or correction is welcome!
- Documentation
- MvcMusicStore sample port on Node.Cs
- Packages on Nuget
I believe it's possible, here is an open-source example written in VB.NET and C#:
https://github.com/perrybutler/dotnetsockets/
It uses Event-based Asynchronous Pattern (EAP), IAsyncResult Pattern and thread pool (IOCP). It will serialize/marshal the messages (messages can be any native object such as a class instance) into binary packets, transfer the packets over TCP, and then deserialize/unmarshal the packets at the receiving end so you get your native object to work with. This part is somewhat like Protobuf or RPC.
It was originally developed as a "netcode" for real-time multiplayer gaming, but it can serve many purposes. Unfortunately I never got around to using it. Maybe someone else will.
The source code has a lot of comments so it should be easy to follow. Enjoy!
EDIT: After stress tests and a few optimizations it was able to accept 16,357 clients before reaching OS limits. Here are the results:
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
Now that's with all clients running on localhost, and sending a small message to the server just after connecting. In a subsequent test on a different system, the server was maxing out at just over 64,000 client connections (port limit reached!) at about 2000 per second, consuming 238 MB of RAM.
Here is my example of single-threaded EAP. There are several implementations of EAP in different application types: from simple console app to asp.net app, tcp server and more. They are all build on the tiny framework called SingleSand which theoretically is pluggable to any type of .net application.
In contrast to the previous answers, my implementation acts as a mediator between existing technologies (asp.net, tcp sockets, RabbitMQ) from one side and tasks inside the event loop from other side. So the goal is not to make a pure single-threaded application but to integrate the event loop to existing application technologies. I fully agree with previous post that .net does not support pure single-threaded apps.
精彩评论