Async в Program.cs [duplicate]

raw_input - это форма ввода, которая принимает аргумент в форме строки, тогда как функция ввода принимает значение в зависимости от вашего ввода. Скажем, a = input (5) возвращает a как целое число со значением 5, тогда как a = raw_input (5) возвращает a как строку «5»

326
задан Liam 9 July 2018 в 12:27
поделиться

15 ответов

Как вы обнаружили, в VS11 компилятор запретит метод async Main. Это было разрешено (но никогда не рекомендовано) в VS2010 с Async CTP.

У меня есть последние сообщения в блоге о асинхронных / await и асинхронных консольных программах в конкретный. Вот некоторая справочная информация из вступительного сообщения:

Если «ожидание» видит, что ожидаемое не завершено, оно действует асинхронно. Он сообщает, что он будет продолжать использовать оставшуюся часть метода, когда он завершит, а затем вернет из метода async. Ожидание также захватит текущий контекст, когда он передаст оставшуюся часть метода ожидаемому.

Позже, когда ожидаемый завершается, он выполнит оставшуюся часть асинхронного метода (в пределах захваченного контекста).

Вот почему это проблема в консольных программах с помощью async Main:

Помните из нашего вступительного сообщения, что метод async вернет для своего вызывающего абонента до его завершения. Это отлично работает в приложениях пользовательского интерфейса (метод просто возвращается к циклу событий пользовательского интерфейса) и приложений ASP.NET (метод возвращает поток, но сохраняет его в ожидании). Это не так хорошо работает для консольных программ: Main возвращается в ОС - так что ваша программа выходит.

Одно из решений - предоставить свой собственный контекст - «основной цикл» для вашего консольная программа с асинхронной совместимостью.

Если у вас есть машина с Async CTP, вы можете использовать GeneralThreadAffineContext из My Documents \ Microsoft Visual Studio Async CTP \ Samples (C # Testing) Unit Тестирование \ AsyncTestUtilities . В качестве альтернативы вы можете использовать AsyncContext из моего пакета Nito.AsyncEx NuGet .

Вот пример с использованием AsyncContext; GeneralThreadAffineContext имеет почти одинаковое использование:

using Nito.AsyncEx;
class Program
{
    static void Main(string[] args)
    {
        AsyncContext.Run(() => MainAsync(args));
    }

    static async void MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Кроме того, вы можете просто заблокировать основной поток Консоли до завершения асинхронной работы:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).GetAwaiter().GetResult();
    }

    static async Task MainAsync(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Обратите внимание на использование GetAwaiter().GetResult(); это позволяет избежать обертывания AggregateException, которое происходит, если вы используете Wait() или Result.

Обновление, 2017-11-30: Начиная с версии Visual Studio 2017 Update 3 (15.3), язык теперь поддерживает a async Main - если он возвращает Task или Task<T>. Итак, теперь вы можете сделать это:

class Program
{
    static async Task Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var list = await bs.GetList();
    }
}

Семантика кажется такой же, как стиль GetAwaiter().GetResult() блокировки основного потока. Однако пока нет спецификации языка для C # 7.1, поэтому это только предположение.

265
ответ дан Stephen Cleary 17 August 2018 в 08:53
поделиться
  • 1
    Вы можете использовать простые Wait или Result, и в этом нет ничего плохого. Но имейте в виду, что существуют два важных отличия: 1) все async продолжения выполняются в пуле потоков, а не в основном потоке, и 2) любые исключения завернуты в AggregateException. – Stephen Cleary 22 May 2014 в 15:32
  • 2
    Имел реальную проблему, выясняя это до этого (и вашего блога). Это самый простой способ решить эту проблему, и вы можете установить пакет в консоли nuget с помощью пакета установки Nito.Asyncex & quot; и все готово. – ConstantineK 5 January 2016 в 07:35
  • 3
    @StephenCleary: Спасибо за быстрый ответ Стивена. Я не понимаю, почему кто-либо не хотел хотеть, чтобы отладчик прерывался при вызове исключения. Если я отлаживаю и запускаю исключение нулевой ссылки, предпочтение отдается прямому нарушению строки кода. VS работает так, как это «из коробки». для синхронного кода, но не для async / wait. – Greg 21 August 2017 в 19:51
  • 4
    На C # 7.1 теперь есть async main, возможно, стоит добавить к вашему большому ответу: @StephenCleary github.com/dotnet/csharplang/blob/master/proposals/csharp-7.1/… – Mafii 30 November 2017 в 15:19
  • 5
    @greg Вероятно, потому что в некоторых библиотеках используются исключения для потока управления , и в этом случае вы не хотите прерывать исключения, потому что будет много, и все они будут «ожидаемыми» или частью Нормальная операция. Однако VS обычно решает эту проблему с помощью функции Just My Code. – binki 1 March 2018 в 16:30

Вы можете решить эту проблему с помощью этой простой конструкции:

class Program
{
    static void Main(string[] args)
    {
        Task.Run(async () =>
        {
            // Do any async anything you need here without worry
        }).GetAwaiter().GetResult();
    }
}

Это поместит все, что вы делаете, на ThreadPool, где вы захотите (так что другие Задачи, которые вы запускаете / ожидаете, не пытаются для воссоединения нити они не должны), и дождитесь, пока все будет сделано до закрытия приложения консоли. Нет необходимости в специальных циклах или вне libs.

Изменить: включить решение Андрея для неперехваченных исключений.

306
ответ дан Chris Moschini 17 August 2018 в 08:53
поделиться
  • 1
    Этот подход очень очевиден, но имеет тенденцию обертывать исключения, поэтому я сейчас ищу лучший способ. – abatishchev 28 January 2015 в 18:18
  • 2
    @abatishchev Вы должны использовать try / catch в своем коде, по крайней мере внутри Task.Run, если не более подробно, не позволяя исключениям плавать до Задачи. Вы избежите проблемы с оберткой, поставив try / catch на вещи, которые могут потерпеть неудачу. – Chris Moschini 23 March 2015 в 23:25
  • 3
    Если вы замените Wait() на GetAwaiter().GetResult(), вы избегаете обертку AggregateException, когда вещи бросаются. – Andrew Arnott 20 November 2015 в 00:26
  • 4
    Вот как async main вводится в C # 7.1, начиная с этой записи. – user9993 14 June 2017 в 12:03

В MSDN документация для Task.Run Method (Action) приведена в этом примере, где показано, как асинхронно запускать метод из 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

Примечание. это утверждение, которое следует примеру:

Примеры показывают, что асинхронная задача выполняется в другом потоке, чем основной поток приложения.

Итак, если вместо этого вы хотите, чтобы задача выполнялась в основном потоке приложения, см. ответ на @StephenCleary .

И в отношении потока, на котором выполняется задача, также обратите внимание на комментарий Стивена :

Вы можете использовать простые Wait или Result, и нет ничего плохого в том, что. Но имейте в виду, что существуют два важных отличия: 1) все продолжения async выполняются в пуле потоков, а не в основном потоке, и 2) любые исключения завернуты в AggregateException.

(см. Обработка исключений (параллельная библиотека задач) для того, как включить обработку исключений для работы с AggregateException.)


Наконец, в MSDN из документации для метода Task.Delay (TimeSpan) в этом примере показано, как запустить асинхронную задачу, которая возвращает значение:

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

Обратите внимание, что вместо передачи delegate в Task.Run, вы можете вместо этого передать лямбда-функцию следующим образом:

var t = Task.Run(async () =>
        {
            await Task.Delay(TimeSpan.FromSeconds(1.5));
            return 42;
        });
3
ответ дан Community 17 August 2018 в 08:53
поделиться

Я добавлю важную особенность, которую игнорировали все остальные ответы: отмена.

. Одна из больших вещей в TPL - поддержка отмены, а в консольных приложениях есть метод отмены, встроенный ( CTRL + C). Их очень просто связать. Вот как я структурирую все мои асинхронные консольные приложения:

static void Main(string[] args)
{
    CancellationTokenSource cts = new CancellationTokenSource();

    System.Console.CancelKeyPress += (s, e) =>
    {
        e.Cancel = true;
        cts.Cancel();
    };

    MainAsync(args, cts.Token).Wait();
}

static async Task MainAsync(string[] args, CancellationToken token)
{
    ...
}
67
ответ дан Cory Nelson 17 August 2018 в 08:53
поделиться
  • 1
    Должна ли маркер отмены передавать и на Wait()? – Siewers 14 October 2014 в 20:03
  • 2
    Нет, потому что вы хотите, чтобы асинхронный код мог грамотно обрабатывать отмену. Если вы передадите его в Wait(), он не будет ждать завершения асинхронного кода - он перестанет ждать и немедленно завершит процесс. – Cory Nelson 14 October 2014 в 20:57
  • 3
    Вы уверены, что? Я только что попробовал это, и кажется, что запрос отмены обрабатывается на самом глубоком уровне, даже когда метод Wait() передается одним и тем же токеном. Я пытаюсь сказать, что это, похоже, не имеет никакого значения. – Siewers 14 October 2014 в 21:00
  • 4
    Я уверен. Вы хотите отменить сам оператор, а не ждать завершения операции op. Если вы не заботитесь о завершении кода очистки или о его результате. – Cory Nelson 14 October 2014 в 22:24
  • 5
    Да, я думаю, я понял, это просто не изменило мой код. Еще одна вещь, которая отбросила меня из курса, - это вежливый намек ReSharper о методе ожидания, поддерживающий отмену;) Возможно, вы захотите включить try catch в пример, поскольку он будет генерировать исключение OperationCancelledException, которое я не мог понять вначале – Siewers 14 October 2014 в 23:14

В главном случае попробуйте изменить вызов GetList на:

Task.Run(() => bs.GetList());
4
ответ дан Fredrik Ljung 17 August 2018 в 08:53
поделиться

Этого еще не нужно, но когда я использовал консольное приложение для быстрых тестов и требуемого async, я просто решил его следующим образом:

class Program
{
    static void Main(string[] args)
    {
        MainAsync(args).Wait();
    }

    static async Task MainAsync(string[] args)
    {
        // Code here
    }
}
18
ответ дан Johan Falk 17 August 2018 в 08:53
поделиться
  • 1
    Этот пример будет работать неправильно, если вам нужно запланировать задачу в текущем контексте и затем ждать ее (например, вы можете забыть добавить ConfigureAwait (false), поэтому метод возврата будет запланирован в основной поток, который находится в функции ожидания ). Поскольку текущий поток находится в состоянии ожидания, вы получите тупик. – Manushin Igor 23 September 2015 в 13:54
  • 2
    Неправда, @ManushinIgor. По крайней мере, в этом тривиальном примере нет SynchronizationContext, связанного с основным потоком. Поэтому он не будет заторможен, потому что даже без ConfigureAwait(false) все продолжения будут выполняться на threadpool. – Andrew Arnott 20 November 2015 в 00:27

Когда был введен CTP C # 5, вы, конечно, могли бы отметить Main с async ... хотя, как правило, это не очень хорошая идея. Я полагаю, что это было изменено выпуском VS 2013, чтобы стать ошибкой.

Если вы не запустили какие-либо другие потоки foreground , ваша программа выйдет, когда Main завершает работу, даже если он запустил некоторые фоновые работы.

Что вы действительно пытаетесь сделать? Обратите внимание, что ваш метод GetList() действительно не должен быть асинхронным в настоящий момент - он добавляет дополнительный уровень без какой-либо реальной причины. Это логически эквивалентно (но более сложному):

public Task<List<TvChannel>> GetList()
{
    return new GetPrograms().DownloadTvChannels();
}
4
ответ дан Jon Skeet 17 August 2018 в 08:53
поделиться
  • 1
    Джон, я хочу получить элементы в списке асинхронно, так почему асинхронный метод не подходит для этого метода GetList? Это потому, что мне нужно собирать элементы в списке async ', а не сам список? Когда я пытаюсь отметить метод Main с помощью async, я получаю «не содержит статический основной метод ...». – danielovich 9 February 2012 в 12:28
  • 2
    @ danielovich: Что возвращает DownloadTvChannels()? Предположительно, он возвращает Task<List<TvChannel>>, не так ли? Если нет, маловероятно, что вы сможете его подождать. (Возможно, с учетом шаблона awaiter, но маловероятно.) Что касается метода Main - он все равно должен быть статическим ... вы заменили > модификатор static на модификатор async возможно? – Jon Skeet 9 February 2012 в 12:34
  • 3
    да, он возвращает задачу «Задача». как ты говорил. Независимо от того, как я пытаюсь поместить async в подпись основного метода, он выдает ошибку. Я сижу на VS11 предварительных битах! – danielovich 9 February 2012 в 13:08
  • 4
    @ danielovich: Даже с типом возврата пустоты? Просто public static async void Main() {}? Но если DownloadTvChannels() уже возвращает Task<List<TvChannel>>, предположительно, он уже асинхронный, поэтому вам не нужно добавлять другой слой. Это стоит понять это внимательно. – Jon Skeet 9 February 2012 в 13:09
  • 5
    @nawfal: Оглядываясь назад, я думаю, что это изменилось до выхода VS2013. Не уверен, что C # 7 изменит это ... – Jon Skeet 21 November 2016 в 09:50

Самая новая версия C # - C # 7.1 позволяет создавать консольное приложение async. Чтобы включить C # 7.1 в проекте, вам необходимо обновить VS до не менее 15.3 и изменить версию C # на C# 7.1 или C# latest minor version. Для этого перейдите в Свойства проекта -> Сборка -> Дополнительно -> Языковая версия.

После этого будет работать следующий код:

internal class Program
{
    public static async Task Main(string[] args)
    {
         (...)
    }
3
ответ дан Kedrzu 17 August 2018 в 08:53
поделиться

C # 7.1 (с использованием vs 2017 update 3) вводит async main

Вы можете написать:

   static async Task Main(string[] args)
  {
    await ...
  }

Подробнее C # 7 Series, часть 2: Async Main

Обновление:

Вы можете получить ошибку компиляции:

Программа не содержит статический «Основной» метод, подходящий для точка входа

Эта ошибка вызвана тем, что vs2017.3 настроен по умолчанию как c # 7.0, а не c # 7.1.

Вы должны явно изменить настройку ваш проект установит функции c # 7.1.

Вы можете установить c # 7.1 двумя способами:

Способ 1: Использование окна настроек проекта:

  • Откройте настройки вашего проекта
  • Выберите вкладку «Сборка»
  • Нажмите кнопку «Дополнительно»
  • Выберите нужную версию. Как показано на следующем рисунке:

Способ2: Изменить свойствоGroup .csproj вручную

Добавить это свойство:

    <LangVersion>7.1</LangVersion>

пример:

    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <PlatformTarget>AnyCPU</PlatformTarget>
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <Optimize>false</Optimize>
        <OutputPath>bin\Debug\</OutputPath>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <Prefer32Bit>false</Prefer32Bit>
        <LangVersion>7.1</LangVersion>
    </PropertyGroup>    
14
ответ дан M.Hassan 17 August 2018 в 08:53
поделиться

В C # 7.1 вы сможете сделать правильный async Main . Соответствующие сигнатуры для метода Main были расширены до:

public static Task Main();
public static Task<int> Main();
public static Task Main(string[] args);
public static Task<int> Main(string[] args);

. вы можете делать:

static async Task Main(string[] args)
{
    Bootstrapper bs = new Bootstrapper();
    var list = await bs.GetList();
}

Во время компиляции метод точки асинхронного ввода будет переведен для вызова GetAwaitor().GetResult().

Подробности: https: // blogs .msdn.microsoft.com / mazhou / 2017/05/30 / c-7-series-part-2-async-main

EDIT:

Чтобы включить C # 7.1, вам нужно щелкнуть правой кнопкой мыши по проекту и нажать «Свойства», затем перейти на вкладку «Построить». Там нажмите кнопку «Дополнительно» внизу:

В раскрывающемся меню языковой версии выберите «7.1» (или любое более высокое значение) :

По умолчанию используется «последняя основная версия», которая будет оценивать (на момент написания этой статьи) C # 7.0, которая не поддерживает async в консольных приложениях.

47
ответ дан Mahmoud Al-Qudsi 17 August 2018 в 08:53
поделиться
  • 1
    FWIW это доступно в Visual Studio 15.3 и выше, которое в настоящее время доступно в виде бета-версии / предварительного просмотра отсюда: visualstudio.com/vs/preview – Mahmoud Al-Qudsi 10 July 2017 в 17:25
  • 2
    Подождите минуту ... Я запускаю полностью обновленную установку, и мой последний вариант - 7.1 ... как вы получили 7.2 уже в мае? – Will 15 November 2017 в 22:12
  • 3
    Ответ мог быть моим. Редактирование октября было кем-то еще к моменту, когда я думаю, что 7.2 (предварительный просмотр?), Возможно, был выпущен. – nawfal 16 November 2017 в 04:20
  • 4
    В качестве примера рассмотрим этот репозиторий GitHub: github.com/nicknijenhuis/ChromeCastBackgroundSyncer – Nick N. 14 May 2018 в 10:59
  • 5
    – user230910 22 August 2018 в 04:42
  • 6

Чтобы избежать зависания, когда вы вызываете функцию где-то вниз, стек вызовов, который пытается повторно присоединить текущий поток (который застрял в Wait), вам нужно сделать следующее:

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result;
    }
}

(приведение требуется только для устранения двусмысленности)

1
ответ дан Nathan Phillips 17 August 2018 в 08:53
поделиться
  • 1
    Благодаря; Task.Run не вызывает тупик GetList (). Подождите, этот ответ будет иметь больше upvotes ... – Stefano d'Antonio 31 January 2016 в 00:10

Вы можете сделать это без внешних библиотек, выполнив следующие действия:

class Program
{
    static void Main(string[] args)
    {
        Bootstrapper bs = new Bootstrapper();
        var getListTask = bs.GetList(); // returns the Task<List<TvChannel>>

        Task.WaitAll(getListTask); // block while the task completes

        var list = getListTask.Result;
    }
}
87
ответ дан p.campbell 17 August 2018 в 08:53
поделиться
  • 1
    Имейте в виду, что getListTask.Result также является блокирующим вызовом, поэтому приведенный выше код можно записать без Task.WaitAll(getListTask). – do0g 12 July 2014 в 05:52
  • 2
    Кроме того, если GetList бросает вам придется поймать AggregateException и опросить его исключения, чтобы определить фактическое исключение. Вы можете, однако, вызвать GetAwaiter(), чтобы получить TaskAwaiter для Task, и называть GetResult() на этом, то есть var list = getListTask.GetAwaiter().GetResult();. При получении результата из TaskAwaiter (также блокирующего вызова) любые выброшенные исключения не будут завернуты в AggregateException. – do0g 12 July 2014 в 05:57
  • 3
    .GetAwaiter (). GetResult был ответом, который мне нужен. Это прекрасно работает для того, что я пытался сделать. Вероятно, я буду использовать это и в других местах. – Deathstalker 24 January 2017 в 00:12

Если вы используете C # 7.1 или более позднюю версию, перейдите к ответу nawfal и просто измените тип возврата вашего метода Main на Task или Task<int>. Если вы не:

  • У вас есть async Task MainAsync , как сказал Йохан .
  • Вызовите его .GetAwaiter().GetResult(), чтобы поймать основное исключение , как do0g сказал .
  • Поддержка отмены , как Cory сказал .
  • Вторая CTRL+C должна немедленно прекратить процесс. (Спасибо binki !) [/ ​​G8]
  • Ручка OperationCancelledException - возвращает соответствующий код ошибки.

Окончательный код выглядит следующим образом:

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.
}
15
ответ дан Şafak Gür 17 August 2018 в 08:53
поделиться
  • 1
    Многие прекрасные программы будут отменять CancelKeyPress только в первый раз, так что если вы нажмете ^ C один раз, вы получите изящное завершение работы, но если вы нетерпеливы, второй ^ C прекратит свою работу. С помощью этого решения вам нужно будет вручную убить программу, если она не соблюдает CancellationToken, потому что e.Cancel = true является безусловным. – binki 1 March 2018 в 16:37

Для асинхронной задачи вызова из Main используйте

  1. Task.Run () для .NET 4.5
  2. Task.Factory.StartNew () для .NET 4.0 (май требуется библиотека Microsoft.Bcl.Async для асинхронных и ожидающих ключевых слов)

Подробности: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24 /10229468.aspx

7
ответ дан user3408030 17 August 2018 в 08:53
поделиться

В моем случае у меня был список заданий, которые я хотел запустить в async из моего основного метода, уже давно использовал это в производстве и отлично работает.

static void Main(string[] args)
{
    Task.Run(async () => { await Task.WhenAll(jobslist.Select(nl => RunMulti(nl))); }).GetAwaiter().GetResult();
}
private static async Task RunMulti(List<string> joblist)
{
    await ...
}
1
ответ дан user_v 17 August 2018 в 08:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: