1

For the past several days, I've been trying to figure out why my async method is not working and its a very simple piece of code.

public class EntryPoint
{
    static void Main()
    {
        RunTheTaskAsync();
        //Console.ReadLine(); // if I add this to "hold" the finish of the project, I will see the result, without it I dont
    }

    public async static void RunTheTaskAsync()
    {
        Task<string> task = Concatenator('1', 200000);

        Console.WriteLine("Heeeelllooooooo");
        Console.WriteLine("I am running while Concatenator is concatenating in the background.");
        Console.WriteLine("You will receive the results shortly");
        string result = await task;

        Console.WriteLine("Result is: " + result.Length);
    }

    public static Task<string> Concatenator(char characterToConcatenate, int count)
    {
        Console.WriteLine("Concatenating!");

        return Task<string>.Factory.StartNew(() =>
        {
            string concatenatedString = "";

            for (int i = 0; i < count; i++)
            {
                concatenatedString += characterToConcatenate;
            }

            return concatenatedString;
        });
    }
}

If I run it as it is in the example above, I never get to see the result, I only see the Console.WriteLine's and Press any key to continue.. after that no result. If I uncomment the Console.ReadLine(); everything works as I am expecting it to work and I simply cant understand why!?! The same is true if I add some Thread.Sleep(xxx) or another piece of code that will take longer to execute than the "Concatenator" method. Isn't this one of the issues that await should solve on its own, its kind of implied from its very name. The way I understand it is:

  • The Task method starts executing on the background
  • We proceed with the 3 ConsoleWritelines in the async method
  • Since there is nothing else to do and the task is not completed yet, we get to the await line and we should await for the task to be completed and then proceed with the rest of the code.

It works like this ONLY if have another piece of code inbetween that will take longer than the Task method to execute, or if I use the Console.ReadLine() and I just cant find an explanation about this!

I also have a secondary question regarding the syntax of my implementation. Is this the proper way to create a Task method? By using

public Task<T> SomeMethodName(some, args)
{
    return Task<T>Factory.StartNew(() => 
    { 
        somecode that returns T;
    }); 
}
Darkbound
  • 3,026
  • 7
  • 34
  • 72
  • 2
    shouldn't you ` await RunTheTaskAsync();` to prevent Main from returning befire RunTheTaskAsync finishes? – KMoussa Feb 15 '18 at 17:58
  • 2
    Just to clarify - your question is "Why if I end process before operation is completed I don't get operation results?" – Alexei Levenkov Feb 15 '18 at 17:59
  • `RunTheTaskAsync()` also needs to return a `Task`, not `void`. – nlawalker Feb 15 '18 at 18:00
  • Await just means that the stuff following it waits until the task completes. However, you don’t await RunTheTaskAsync that’s why when you call the task starts, but immediately afterwards the caller just proceeds without waiting and the application exits. RunTheTaskAsync also needs to return a Task - never use async void except for event handlers and make Main also async. – ckuri Feb 15 '18 at 18:01
  • @AlexeiLevenkov that would have probably been my question, if I understood the process :) – Darkbound Feb 15 '18 at 18:09
  • 1
    Why is this downvoted? It's a clear, concrete problem and even includes an mcve. – Heinzi Feb 15 '18 at 18:10
  • Regarding your secondary question: use Task.FromResult to wrap a return value within a task. There is no need to spawn a new task. – ckuri Feb 15 '18 at 18:23
  • 3
    The more fundamental problem here is: you're trying to understand how asynchronous code works from a console application. That's a bad idea. Write a GUI application and make it asynchronous; GUIs are designed from the beginning to support asynchronous workflows because they have a message loop. – Eric Lippert Feb 15 '18 at 18:24
  • Possible duplicate of [C# async/await strange behavior in console app](https://stackoverflow.com/questions/31564204/c-sharp-async-await-strange-behavior-in-console-app) – JSteward Feb 15 '18 at 18:32

1 Answers1

4

async voids (like your RunTheTaskAsync method) are "fire and forget". You can't track their progress and almost never want to use them.

Instead, make an async Task and await it:

static async Task Main()
{
    await RunTheTaskAsync();
}

public static async Task RunTheTaskAsync()
{
    ...
}

For more details, see:

Note: Visual Studio prior to 2017 Update 3 does not support an async Main method. In that case, you have to manually await the Task:

static void Main()
{
    RunTheTaskAsync().GetAwaiter().Wait();
}

About your second question (for the future: SO's policy is "one question per question"): Yes, Task.Factory.StartNew is correct if you need your task to run in a separate thread. Newer versions of .NET as offer Task.Run as a shortcut, see the following question for details:

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • 1
    Main must also be async Task to be able to await RunTheTaskAsync. Or before C# 7 were async main is not supported one should use `RunTheTaskAsync().GetAwaiter().Wait();` – ckuri Feb 15 '18 at 18:02
  • @Heinzi thanks, I managed to make it work. But I have a few new questions now. 1. The Code will execute asynchronously only within an async method, and the code that is outside of the method will still be ran synchronously and wait for the async method to finish, in this example cw("Haha") does not appear before the result, so it is indeed waiting for the async method to finish first, screenshot -> http://prntscr.com/if8b2e I guess that this question only requires a True/False answer :D – Darkbound Feb 15 '18 at 18:16
  • @Darkbound: Well, invoking `.Result` causes your code to block until the result is available. Thus, you should not do it until you actually *need* the result to continue. The correct way would be: `Task t = RunTheTaskAsync(); Console.WriteLine("haha"); Console.WriteLine(t.Result);`. – Heinzi Feb 15 '18 at 18:20
  • @Heinzi I see, so by doing what I am doing, I am "forcing" the code in the Main method to run synchronously, but the code within the async method will still run asynchronously – Darkbound Feb 15 '18 at 18:21
  • @Heinzi http://prntscr.com/if8mh8 would you say that this is a better implementation of what I was trying to do? Seems much more cleaner to me and is working as I expect it to work. – Darkbound Feb 15 '18 at 18:37
  • @Darkbound: Looks fine. You can make it even simpler by removing `async` from your method and `await` from your return statement. – Heinzi Feb 15 '18 at 18:41