How competition works on a single thread with C#

Asked

Viewed 175 times

5

This is a purely didactic question.

I’m trying to understand how the competition model works in C#, specifically by using the async modifier.

I come from Javascript, and I understand that in the JS model, competition is possible thanks to Event-loop. In this model we have a main loop, in which all the procedures are placed in a queue. When it is time for a procedure in this queue to be executed, if it is in a pending state, it is pushed to the end of the queue, so other procedures can be performed without the main process entering an idle state.

What happens is that the code is executed alternately, here’s the example I have:

async function main() {
  var promise1 = loop();
  var promise2 = loop();
  
  await promise1;
  await promise2;
}

async function loop() {
  for (var i = 0; i < 5; i++) {
    console.log(i);
    await null;
  }
}

main();

async and await are not Javascript-only features, but other languages that implement this feature do not always run natively on an Event-loop. In Python for example when an async function is invoked, it only returns a co-routine, which needs to be placed in an Event-loop created by the programmer with asyncio.run, and then executed.


But in C# I notice that the way it behaves is a little different compared to Javascript or Python. Using the following code as an example:

public static void Main()
{
    AsyncMain().Wait();
}

public static async Task AsyncMain()
{
    var task1 = Loop();
    var task2 = Loop();

    await task1;
    await task2;
}

public static async Task Loop()
{
    for (var i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
        await Task.Delay(1);
    }
}

The first thing I notice is that even though C# doesn’t run on an Event-loop natively, at no point am I creating my own Event-loop. Another aspect that you can only notice by running the code, is that it will not print in the console exactly alternately as it would in Javascript or Python, instead of printing 0 0 1 1 2 2 3 3 4 4, it will print something like 0 0 1 2 1 3 2 4 3 4.

What’s going on here?

  • C# creates its own Event-loop implicitly, or it works with another model?

  • Why in C# the queue of procedures does not seem to be a deterministic process?

1 answer

5

Good afternoon Julio

In fact there are differences. The . Net works based on Threadpool and this has a global queue in the FIFO model. Depending on the size of this global queue, more threads are being raised to give flow (If you are using dotnet core, a cool way to view this live is by using dotnet-counters [https://docs.microsoft.com/pt-br/dotnet/core/diagnostics/dotnet-counters]).

Starting with CLR 4, each Thread also has its own LIFO line. Depending on how Thread pulls that task, it may or may not go to the Thread queue (I’ll send you a link explaining more about it). But it’s interesting that when a thread has some task locking its queue, another thread that is unoccupied can steal the next task from the locked thread (remembering that it will receive this task in the LIFO model), to help it flow. I understand that’s why you don’t have much control over the order in which things run on the async model.

I’ll send you some interesting sources:

Browser other questions tagged

You are not signed in. Login or sign up in order to post.