I am new to asynchronous programming with the async
modifier. I am trying to figure out how to make sure that my Main
method of a console applicati
For asynchronously calling task from Main, use
Task.Run() for .NET 4.5
Task.Factory.StartNew() for .NET 4.0 (May require Microsoft.Bcl.Async library for async and await keywords)
Details: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
On MSDN, the documentation for Task.Run Method (Action) provides this example which shows how to run a method asynchronously from main
:
using System;
using System.Threading;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
ShowThreadInfo("Application");
var t = Task.Run(() => ShowThreadInfo("Task") );
t.Wait();
}
static void ShowThreadInfo(String s)
{
Console.WriteLine("{0} Thread ID: {1}",
s, Thread.CurrentThread.ManagedThreadId);
}
}
// The example displays the following output:
// Application thread ID: 1
// Task thread ID: 3
Note this statement that follows the example:
The examples show that the asynchronous task executes on a different thread than the main application thread.
So, if instead you want the task to run on the main application thread, see the answer by @StephenCleary.
And regarding the thread on which the task runs, also note Stephen's comment on his answer:
You can use a simple
Wait
orResult
, and there's nothing wrong with that. But be aware that there are two important differences: 1) allasync
continuations run on the thread pool rather than the main thread, and 2) any exceptions are wrapped in anAggregateException
.
(See Exception Handling (Task Parallel Library) for how to incorporate exception handling to deal with an AggregateException
.)
Finally, on MSDN from the documentation for Task.Delay Method (TimeSpan), this example shows how to run an asynchronous task that returns a value:
using System;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
var t = Task.Run(async delegate
{
await Task.Delay(TimeSpan.FromSeconds(1.5));
return 42;
});
t.Wait();
Console.WriteLine("Task t Status: {0}, Result: {1}",
t.Status, t.Result);
}
}
// The example displays the following output:
// Task t Status: RanToCompletion, Result: 42
Note that instead of passing a delegate
to Task.Run
, you can instead pass a lambda function like this:
var t = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(1.5));
return 42;
});
You can solve this with this simple construct:
class Program
{
static void Main(string[] args)
{
Task.Run(async () =>
{
// Do any async anything you need here without worry
}).GetAwaiter().GetResult();
}
}
That will put everything you do out on the ThreadPool where you'd want it (so other Tasks you start/await don't attempt to rejoin a Thread they shouldn't), and wait until everything's done before closing the Console app. No need for special loops or outside libs.
Edit: Incorporate Andrew's solution for uncaught Exceptions.
To avoid freezing when you call a function somewhere down the call stack that tries to re-join the current thread (which is stuck in a Wait), you need to do the following:
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result;
}
}
(the cast is only required to resolve ambiguity)
If you're using C# 7.1 or later, go with the nawfal's answer and just change the return type of your Main method to Task
or Task<int>
. If you are not:
async Task MainAsync
like Johan said..GetAwaiter().GetResult()
to catch the underlying exception like do0g said.CTRL+C
should terminate the process immediately. (Thanks binki!)OperationCancelledException
- return an appropriate error code.The final code looks like:
private static int Main(string[] args)
{
var cts = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = !cts.IsCancellationRequested;
cts.Cancel();
};
try
{
return MainAsync(args, cts.Token).GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
return 1223; // Cancelled.
}
}
private static async Task<int> MainAsync(string[] args, CancellationToken cancellationToken)
{
// Your code...
return await Task.FromResult(0); // Success.
}
When the C# 5 CTP was introduced, you certainly could mark Main with async
... although it was generally not a good idea to do so. I believe this was changed by the release of VS 2013 to become an error.
Unless you've started any other foreground threads, your program will exit when Main
completes, even if it's started some background work.
What are you really trying to do? Note that your GetList()
method really doesn't need to be async at the moment - it's adding an extra layer for no real reason. It's logically equivalent to (but more complicated than):
public Task<List<TvChannel>> GetList()
{
return new GetPrograms().DownloadTvChannels();
}