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 multithreading.
C# is a programming language and . NET is a framework. . NET has Common Language Runtime (CLR), which is a virtual component of .
C# (pronounced “see-sharp”) is the actual coding language that we write inside of the . NET framework.
You can program in C++ using visual studio . NET. Well that's what it was called along time ago.
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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With