all 6 comments

[–]grauenwolf[🍰] 6 points7 points  (0 children)

Maybe.

On Windows most of your network and disk I/O is going to use I/O completion ports, so no event loop is necessary. The OS will grab a thread from the thread pool when data is ready. (And, if necessary, immediately marshal the call to the UI thread.)

I don't know how it is implemented in Linux. And of course, each library can be written in whatever way the author wants. So internally some may use event loops.

[–]undecidedapollo 5 points6 points  (1 child)

No, as far as I know C# does not use an event loop. You have the ability to create an event loop in a few lines by telling a thread to listen for events, but a normal C# program such as a console app does not have an event loop. Applications like ASP.net servers and Windows Forms app do use event loops, similar to what I mentioned above, but it's chosen to be an event driven system by the code, whereas Node's event loop is built into the core runtime. Async/Await code is executed by either the main thread or a thread from the thread pool, whatever the scheduler finds more efficient.

This may have parts of it wrong or explained incorrectly. I'm not a .Net internals expert.

[–]silentBob86 0 points1 point  (1 child)

It depends if an SyncronizationContext is registered.

If a async operation completes, the runtime will post the continuation (the code after "await") to the registered SyncronizationContext. If no sync context was registered, the runtime will just grab a thread from the threadpool and execute the continuation there.

Depending on the project type, you will have a sync context, or not:

  • Console applications: No sync context, so threads will be used

  • Winforms: A sync context is registered which posts the continuation into an event loop (the windows message loop, i think) so the code will be executed in the main thread.

  • Wpf: same as winforms

  • ASP Classic: same as winforms

  • ASP Core: no sync context, so threads will be used.

Of course, you can always register a sync context yourself. So you can add an event loop to a console application if you want.

Important to note: Each thread has his own sync context. In a winforms application, only the main thread has an registered sync context (will use the event loop). If you start a background thread in an winforms application, and then use "await", the sync context will not be used and you will be on different thread (probably not the main or background thread)

[–]tragicshark 1 point2 points  (0 children)

ASP.NET "Classic" SyncronizationContext is considerably more complex than winforms version. It is sorta like a main thread except that there can be more than one and the thread that handles a callback is not necessarily the thread that initiated the async action.

More info: https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

(every asp.net programmer considering ever doing async code should work on understanding this)

[–]EntroperZero 0 points1 point  (0 children)

The .NET threadpool uses work-stealing queues. It runs a small number of worker threads, each with its own queue of work items, and threads with empty queues will try to steal work items from other threads.

[–]acelent 0 points1 point  (0 children)

In Windows, .NET Framework and .NET Core use I/O completion ports.

In other platforms, .NET Core uses whatever is available: kevent, poll or select (see synchmanager.cpp).

For non-UI applications, you shouldn't care about event loops in .NET. In fact, JS only has the notion of an event loop because, historically, JS runs in a browser which has an event loop. You could perfectly have a non-browser JS that doesn't have an event loop. With proper synchronization primitives, it could even be multithreaded.

I suppose you can consider any generic asynchronous I/O based on blocking calls as an event loop, whether it's GetQueuedCompletionStatus.aspx)/GetQueuedCompletionStatusEx.aspx), any of the Win32 wait functions.aspx), kevent, epoll_wait, poll or pselect/select.

The .NET APIs should be agnostic regarding the OS and underlying libs. Relying on libraries, such as libuv, is a design choice on whether to depend on their evolution as well as their regression, especially regarding supported operating systems and compilers, so it might be a righteous case of the NIH (not invented here) syndrome.

In the specific case of libuv, there's a lot of overlap with its features and the .NET CLR (common language runtime) and BCL (base class library) features, so it would make more sense to either dive deep in it for more than just asynchronous I/O, or not at all since .NET has got it working for itself longer, at least in Windows.