raw_input - это форма ввода, которая принимает аргумент в форме строки, тогда как функция ввода принимает значение в зависимости от вашего ввода. Скажем, a = input (5) возвращает a как целое число со значением 5, тогда как a = raw_input (5) возвращает a как строку «5»
Как вы обнаружили, в 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, поэтому это только предположение.
Вы можете решить эту проблему с помощью этой простой конструкции:
class Program
{
static void Main(string[] args)
{
Task.Run(async () =>
{
// Do any async anything you need here without worry
}).GetAwaiter().GetResult();
}
}
Это поместит все, что вы делаете, на ThreadPool, где вы захотите (так что другие Задачи, которые вы запускаете / ожидаете, не пытаются для воссоединения нити они не должны), и дождитесь, пока все будет сделано до закрытия приложения консоли. Нет необходимости в специальных циклах или вне libs.
Изменить: включить решение Андрея для неперехваченных исключений.
Wait()
на GetAwaiter().GetResult()
, вы избегаете обертку AggregateException
, когда вещи бросаются.
– Andrew Arnott
20 November 2015 в 00:26
В 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; });
Я добавлю важную особенность, которую игнорировали все остальные ответы: отмена.
. Одна из больших вещей в 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)
{
...
}
Wait()
, он не будет ждать завершения асинхронного кода - он перестанет ждать и немедленно завершит процесс.
– Cory Nelson
14 October 2014 в 20:57
Wait()
передается одним и тем же токеном. Я пытаюсь сказать, что это, похоже, не имеет никакого значения.
– Siewers
14 October 2014 в 21:00
В главном случае попробуйте изменить вызов GetList на:
Task.Run(() => bs.GetList());
Этого еще не нужно, но когда я использовал консольное приложение для быстрых тестов и требуемого async, я просто решил его следующим образом:
class Program
{
static void Main(string[] args)
{
MainAsync(args).Wait();
}
static async Task MainAsync(string[] args)
{
// Code here
}
}
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();
}
DownloadTvChannels()
? Предположительно, он возвращает Task<List<TvChannel>>
, не так ли? Если нет, маловероятно, что вы сможете его подождать. (Возможно, с учетом шаблона awaiter, но маловероятно.) Что касается метода Main
- он все равно должен быть статическим ... вы заменили >> модификатор static
на модификатор async
возможно?
– Jon Skeet
9 February 2012 в 12:34
public static async void Main() {}
? Но если DownloadTvChannels()
уже возвращает Task<List<TvChannel>>
, предположительно, он уже асинхронный, поэтому вам не нужно добавлять другой слой. Это стоит понять это внимательно.
– Jon Skeet
9 February 2012 в 13:09
Самая новая версия 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)
{
(...)
}
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>
В 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 в консольных приложениях.
Чтобы избежать зависания, когда вы вызываете функцию где-то вниз, стек вызовов, который пытается повторно присоединить текущий поток (который застрял в Wait), вам нужно сделать следующее:
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result;
}
}
(приведение требуется только для устранения двусмысленности)
Вы можете сделать это без внешних библиотек, выполнив следующие действия:
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;
}
}
getListTask.Result
также является блокирующим вызовом, поэтому приведенный выше код можно записать без Task.WaitAll(getListTask)
.
– do0g
12 July 2014 в 05:52
GetList
бросает вам придется поймать AggregateException
и опросить его исключения, чтобы определить фактическое исключение. Вы можете, однако, вызвать GetAwaiter()
, чтобы получить TaskAwaiter
для Task
, и называть GetResult()
на этом, то есть var list = getListTask.GetAwaiter().GetResult();
. При получении результата из TaskAwaiter
(также блокирующего вызова) любые выброшенные исключения не будут завернуты в AggregateException
.
– do0g
12 July 2014 в 05:57
Если вы используете C # 7.1 или более позднюю версию, перейдите к ответу nawfal и просто измените тип возврата вашего метода Main на Task
или Task<int>
. Если вы не:
async Task MainAsync
, как сказал Йохан . .GetAwaiter().GetResult()
, чтобы поймать основное исключение , как do0g сказал . 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.
}
e.Cancel = true
является безусловным.
– binki
1 March 2018 в 16:37
Для асинхронной задачи вызова из Main используйте
Подробности: http://blogs.msdn.com/b/pfxteam/archive/2011/10/24 /10229468.aspx
В моем случае у меня был список заданий, которые я хотел запустить в 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 ...
}
Wait
илиResult
, и в этом нет ничего плохого. Но имейте в виду, что существуют два важных отличия: 1) всеasync
продолжения выполняются в пуле потоков, а не в основном потоке, и 2) любые исключения завернуты вAggregateException
. – Stephen Cleary 22 May 2014 в 15:32