Can't specify the 'async' modifier on the 'Main' method of a console app

后端 未结 16 2013
后悔当初
后悔当初 2020-11-21 07:44

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

相关标签:
16条回答
  • 2020-11-21 08:24

    For asynchronously calling task from Main, use

    1. Task.Run() for .NET 4.5

    2. 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

    0 讨论(0)
  • 2020-11-21 08:25

    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 or Result, and there's nothing wrong with that. But be aware that there are two important differences: 1) all async continuations run on the thread pool rather than the main thread, and 2) any exceptions are wrapped in an AggregateException.

    (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;
            });
    
    0 讨论(0)
  • 2020-11-21 08:26

    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.

    0 讨论(0)
  • 2020-11-21 08:27

    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)

    0 讨论(0)
  • 2020-11-21 08:30

    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:

    • Have an async Task MainAsync like Johan said.
    • Call its .GetAwaiter().GetResult() to catch the underlying exception like do0g said.
    • Support cancellation like Cory said.
    • A second CTRL+C should terminate the process immediately. (Thanks binki!)
    • Handle 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.
    }
    
    0 讨论(0)
  • 2020-11-21 08:31

    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();
    }
    
    0 讨论(0)
提交回复
热议问题