-2

Assuming that I need to have some concurrent processes and I would like to forget about the old Thread class and stick to Task with an async pattern. Given the following codes:

// Scenario 1
public async Task Accept() {
    while(true) {
        Connection con = await connection.AcceptAsync();
        await HandleConnection(con);
    }
}

public Task HandleConnection(Connection con) {

   // Long Running Logic
   return Task.CompletedTask;
}

// Scenario 2
public async Task Accept() {
    while(true) {
        Connection con = await connection.AcceptAsync();
        HandleConnection(con); // Produces a warning
    }
}

public Task HandleConnection(Connection con) {

   // Long Running Logic
   return Task.CompletedTask;
}

Both these approaches fail and I cannot handle the connection concurrently. For example when the first connection is accepted then I cannot accept the second connection until the HandleConnection method finishes its job. To solve this problem I can do the following:

public async Task Accept() {
    while(true) {
        Connection con = await connection.AcceptAsync();
        HandleConnection(con); // Produces a warning
    }
}

public Task HandleConnection(Connection con) {
    return Task.Run(()=> {
        //Long Running Logic
    });
}

Now I am able to handle multiple connections but this behavior raises a few questions:

1- I heard that the await keyword in contrast with wait is non-blocking and in fact the whole async pattern in non-blocking as well. But in this situation it is actually blocking the parent thread.

2- When a method is async, a Task resembling that method must be generated as a result so that it can be awaited, is that true? if that is true then why is it blocking? else how does the await mechanism work?

3- If I don't await the Task, I get a compile time warning which says I have to await the task, but if I use it then the parent thread is blocked.

To ditch the warning I can do HandleConnection(con).Wait(0) but isn't that a bad practice?

Arnold Zahrneinder
  • 4,788
  • 10
  • 40
  • 76

1 Answers1

-2

I played with this issue and tested that thoroughly. The conclusion is that in a method that returns a Task, if you do not actually return a Task or instead await or return Task.CompletedTask, that method will still run on the Parent thread and will surely block it. You can only benefit from multithreading if you actually raise a Task as I represented in my question.

To prove this, the following is blocking:

public static void Main(String[] args) {
    test();
    Console.WriteLine("Hello World");
}
private static Task test() {
    while(true) {}
    return Task.CompletedTask;
}

In this code test will block the main thread.

If you make the test async, it will still block the main thread.

public static void Main(String[] args) {
    test();
    Console.WriteLine("Hello World");
}
private static async Task test() {
    while(true) {}
}

This is while the following does not block the main thread:

public static async Task test() {
        await Task.Run(()=> {
            while(true) {}
        });
    }

However, if you don't want to await the Task you should raise the Task in void method:

public void Method() {
     Task.Run(()=> ());
}

This is much better than Task.Wait(0).

Arnold Zahrneinder
  • 4,788
  • 10
  • 40
  • 76
  • 3
    1) Use `await Task.Yield();` to force an async method to actually be async. 2) Your "non-blocking" `test()` _is_ blocking! Tasks are designed to be _awaited_, and your `test()` never completes. Anyone who awaits it will be blocked forever. You must not design tasks that never complete, and that is fundamentally the source of your problems: you are shooting yourself in the foot by awaiting a task in the middle of your program that will never complete. – Jeff Sep 28 '20 at 14:51
  • @Jeff: That is absolutely right and I agree, I just wanna demonstrate what actually happens. – Arnold Zahrneinder Sep 28 '20 at 14:52
  • @Jeff: However, `Use await Task.Yield(); to force an async method to actually be async` is not right. You just keep the method signature using that. – Arnold Zahrneinder Sep 28 '20 at 14:54