15

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jeff
  • 13,079
  • 23
  • 71
  • 102
  • Does `BeginRead` (http://msdn.microsoft.com/en-us/library/system.io.stream.beginread.aspx) accomplish that? – Gabe May 16 '10 at 01:14
  • 1
    All network I/O on Windows could be asynchronous, event-based, and without multithreading in the 16-bit days because Windows programming is event-driven and Win16 wasn't multithreaded. – Gabe May 16 '10 at 01:17
  • @Gabe, please see my comment against @Jeremy's answer. – Jeff May 16 '10 at 01:18
  • This question has nothing to do with C#. C# is a programming language that does not have IO or threads. – John Saunders May 16 '10 at 01:53
  • 2
    @John: I think that relating it to C# is relevant - there are some C# features that enable more interesting asynchronous models (e.g. `yield return` and CCR). – Tomas Petricek May 16 '10 at 02:02
  • @Tomas: Did the OP know about yield return and CCR when he put "C#" into the title, or is the OP simply one of the millions of people who don't distinguish between C# and the .NET Framework? – John Saunders May 17 '10 at 03:17
  • @John: He's probably using C#, so that's why he put it there... Nevertheless, we can use it to give better answer... – Tomas Petricek May 17 '10 at 07:58
  • @Tomas: there's been somewhat of a consensus that tags should not be repeated in the subject line. Can you give a better answer with the language in the subject than you can with the language in the tags? – John Saunders May 17 '10 at 19:54
  • @John, of course I know about keywords like 'yield return' it's just that I wanted to know the answer specifically in C# syntax and or specific C# implementations. But I will pick the best answer suits me in the end. But I yet to see one that's acceptable which explains the concept and given concrete example. Actually some of the answers made it more confusing. – Jeff May 17 '10 at 20:42
  • @John: I don't have any objections at all regarding the change in the subject line! I just thought that leaving the "c#" tag would be useful (perhaps this is just some misunderstanding?) – Tomas Petricek May 17 '10 at 22:04
  • @Tomas: although I'm sceptical that the solution to the OP's problem will turn out to be specific to C#, you are correct. In cases like these I usually remove the tags from the title and leave them in the tags. In this case, I was mistaken to remove the C# tag, as it indicates the language the OP wants the answers in. – John Saunders May 17 '10 at 22:35
  • @John: No problem, leaving tag & removing it from the title makes perfect sense! Regarding C# specific solution - you can use this technique [ http://tomasp.net/blog/csharp-async.aspx ] to write asynchronous computations in C# and this can be quite effectively used for simulating this [ http://dotnetslackers.com/articles/net/Programming-user-interfaces-using-f-sharp-workflows.aspx ] in C#. Putting this together would require some work, but it would definitely be (as nice as possible) C# specific solution to that problem if someone did the missing piece of work. – Tomas Petricek May 17 '10 at 23:31

10 Answers10

13

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 :-)

Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
8

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.

Oskar Berggren
  • 5,583
  • 1
  • 19
  • 36
Ben Lesh
  • 107,825
  • 47
  • 247
  • 232
6

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.

Mark Brackett
  • 84,552
  • 17
  • 108
  • 152
  • is your example non-blocking and asynchronous? All I see is a program that iterate through a list of tasks. And the tasks or in the form of closures. – Jeff May 16 '10 at 02:27
  • @Jeffrey - yes. You defer the blocking call by delegating it to the EventLoop to run, which means you can run your own tasks prior to the blocking call. Looking closer at node.js, though - they actually *do* take advantage of multiple threads ("Node tells the operating system (through epoll, kqueue, /dev/poll, or select) that it should be notified when the 2 seconds are up"), they're just not explicitly creating them. – Mark Brackett May 16 '10 at 15:40
  • 1
    I think I am totally confused now. – Jeff May 17 '10 at 01:31
  • Event loop is non-blocking and operate in one thread. Look here http://www.youtube.com/watch?v=QgwSUtYSUqA,so the above implementation is correct node.js is not the only way to implement event loop – LXG Sep 28 '13 at 10:58
3

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.

Richard Anthony Hein
  • 10,550
  • 3
  • 42
  • 62
  • However, Rx is fully multi-threaded, even though, it could in principle support single-threaded programming model too. My implementation of the same idea was single-threaded (but limited in many ways!) http://tomasp.net/blog/reactive-iv-reactivegame.aspx. Also Rx is now available for JavaScript, and that's certainly single-threaded :-). – Tomas Petricek May 16 '10 at 02:01
  • 1
    Since there's a lot of confusion about asynch requiring or implying multithreading, let me make it clear that asynch does not imply or require multithreading, and neither does Rx. See, for example, http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx for information about how this works. – Richard Anthony Hein Feb 25 '11 at 19:18
2

Not sure if this is what you're looking for. .NET can do asynchronous callbacks without explicit multi threading.

Jeremy
  • 4,808
  • 2
  • 21
  • 24
  • 1
    I am not 100% sure but I sort of think under the hood .NET will create new thread to run events asynchronously I might be wrong though. – Jeff May 16 '10 at 01:17
2

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.

Community
  • 1
  • 1
Kiril
  • 39,672
  • 31
  • 167
  • 226
  • Seems I need to get my hands dirty and start deciphering the underlying of libraries like event machine, twisted and node.js. I am interested to really understand how they work. – Jeff May 16 '10 at 01:40
1

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.

Edward Brey
  • 40,302
  • 20
  • 199
  • 253
0

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!

Kendar
  • 692
  • 7
  • 25
0

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.

perry
  • 266
  • 1
  • 6
  • Technically speaking this is still multithreading, it's just that the libraries are doing it for you. – David Moles Aug 12 '14 at 22:55
  • Thanks for clarifying that, you are technically correct. However, it's the same method Node.js uses for handling completions (thread pool / IOCP) in Windows, at least I think so, which would mean Node.js is not truly single threaded either. Therefore I do think that C# is capable of a similar design to Node.js, in an attempt to answer the original question. While thread pool / IOCP does imply using more than a single thread, it is still not considered a "threaded server". What do you think? – perry Aug 12 '14 at 23:13
  • You're right, under the hood Node [does use a C++-based thread pool for long-running tasks](http://rickgaribay.net/archive/2012/01/28/node-is-not-single-threaded.aspx). I wasn't aware. (Suddenly it seems a lot less architecturally interesting...) – David Moles Aug 13 '14 at 17:46
  • That is an excellent article David, I had the pleasure of reading it a while back. Node.js uses libuv, which is a library that abstracts async IO operations across various operating systems, and is what powers the async functionality behind Node.js in Windows, Unix, OSX, etc. If you ask me, this is where the magic in Node.js lies, and the fact that it is Javascript which means client code can easily be ported to the server. – perry Aug 13 '14 at 18:10
  • That said, I think to a lot of people still, Node.js remains a mystical beast. – perry Aug 13 '14 at 18:24
0

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.

neleus
  • 2,230
  • 21
  • 36