콘솔 앱의 '메인'메소드에서 '비동기'수정자를 지정할 수 없습니다.
async
수정자를 사용한 비동기 프로그래밍이 처음 입니다. Main
콘솔 응용 프로그램의 메서드가 실제로 비동기 적으로 실행 되도록하는 방법을 알아 내려고 노력 중 입니다.
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
var list = bs.GetList();
}
}
public class Bootstrapper {
public async Task<List<TvChannel>> GetList()
{
GetPrograms pro = new GetPrograms();
return await pro.DownloadTvChannels();
}
}
나는 이것이 "위에서"비동기 적으로 실행되지 않는다는 것을 알고있다. 메소드에 async
수정자를 지정할 수 없으므로 비동기 적으로 Main
코드를 어떻게 실행할 수 main
있습니까?
알다시피 VS11에서 컴파일러는 async Main
메소드 를 허용하지 않습니다 . 이것은 비동기 CTP와 함께 VS2010에서 허용되었지만 권장되지는 않았습니다.
async / await 및 비동기 콘솔 프로그램 에 대한 최근 블로그 게시물이 있습니다 . 소개 게시물의 배경 정보는 다음과 같습니다.
"await"는 awaitable이 완료되지 않은 것으로 보이면 비동기 적으로 작동합니다. 완료 될 때 메소드의 나머지를 실행하도록 awaitable에 지시 한 다음 async 메소드에서 리턴합니다 . Await는 메소드의 나머지를 awaitable에 전달할 때 현재 컨텍스트를 캡처합니다 .
나중에 Awaitable이 완료되면 나머지 캡처 방법 (캡처 된 컨텍스트 내)을 실행합니다.
이것이 콘솔 프로그램에서 문제가되는 이유는 다음과 같습니다 async Main
.
소개 게시물에서 비동기 메소드는 호출이 완료되기 전에 호출자로 돌아갑니다 . 이것은 UI 응용 프로그램 (메소드가 UI 이벤트 루프로 돌아옴) 및 ASP.NET 응용 프로그램 (메서드가 스레드를 반환하지만 요청은 유지)에서 완벽하게 작동합니다. 콘솔 프로그램의 경우 제대로 작동하지 않습니다. Main이 OS로 돌아가므로 프로그램이 종료됩니다.
솔루션 중 하나는 비동기 호환 콘솔 프로그램에 대한 "메인 루프"라는 고유 한 컨텍스트를 제공하는 것입니다.
당신은 비동기 CTP와 기계가있는 경우 사용할 수 있습니다 GeneralThreadAffineContext
에서 내 문서 \ 마이크로 소프트 비주얼 스튜디오 비동기 CTP \ 샘플 (C # 테스트) 단위 테스트 \ AsyncTestUtilities . 또는 내 Nito.AsyncEx NuGet 패키지AsyncContext
에서 사용할 수 있습니다 .
다음은 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일 : 비주얼 스튜디오 2017 업데이트 3 (15.3) 현재로는, 언어는 지금 지원 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에서 수행 할 수 있습니다 (따라서 시작 / 대기하는 다른 작업은 수행하지 말아야 할 스레드에 다시 참여하려고 시도하지 않음). 콘솔 앱을 닫기 전에 모든 작업이 완료 될 때까지 기다리십시오. 특별한 루프 나 외부 라이브러리가 필요 없습니다.
편집 : 잡히지 않은 예외에 대한 Andrew의 솔루션을 통합하십시오.
다음을 수행하여 외부 라이브러리 없이도이를 수행 할 수 있습니다.
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;
}
}
다른 모든 답변에서 간과 한 중요한 기능인 취소를 추가하겠습니다.
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)
{
...
}
C # 7.1에서는 적절한 비동기 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();
}
컴파일 타임에 비동기 진입 점 메소드는 call로 변환됩니다 GetAwaitor().GetResult()
.
세부 사항 : https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main
편집하다:
C # 7.1 언어 기능을 사용하려면 프로젝트를 마우스 오른쪽 단추로 클릭하고 "속성"을 클릭 한 다음 "빌드"탭으로 이동해야합니다. 하단의 고급 버튼을 클릭하십시오.
언어 버전 드롭 다운 메뉴에서 "7.1"(또는 더 높은 값)을 선택하십시오.
기본값은 "최신 메이저 버전"이며 (이 글을 쓰는 시점에) 콘솔 앱에서 비동기 메인을 지원하지 않는 C # 7.0으로 평가됩니다.
아직 많이 필요하지는 않았지만 빠른 테스트에 콘솔 응용 프로그램을 사용하고 비동기가 필요한 경우 다음과 같이 해결했습니다.
class Program
{
static void Main(string[] args)
{
MainAsync(args).Wait();
}
static async Task MainAsync(string[] args)
{
// Code here
}
}
C # 7.1 (2017 업데이트 3 사용)에 비동기 메인이 도입되었습니다.
당신은 쓸 수 있습니다:
static async Task Main(string[] args)
{
await ...
}
자세한 내용은 C # 7 시리즈, 파트 2 : 비동기 메인
최신 정보:
컴파일 오류가 발생할 수 있습니다.
프로그램에 진입 점에 적합한 정적 '메인'방법이 없습니다.
이 오류는 vs2017.3이 기본적으로 c # 7.1이 아닌 c # 7.0으로 구성되어 있기 때문입니다.
c # 7.1 기능을 설정하려면 프로젝트 설정을 명시 적으로 수정해야합니다.
두 가지 방법으로 c # 7.1을 설정할 수 있습니다.
방법 1 : 프로젝트 설정 창 사용 :
- 프로젝트의 설정을 엽니 다
- 빌드 탭을 선택하십시오
- 고급 버튼을 클릭하십시오
- 다음 그림과 같이 원하는 버전을 선택하십시오.
방법 2 : .csproj의 PropertyGroup을 수동으로 수정
이 속성을 추가하십시오 :
<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 이상을 사용하는 경우 nawfal의 답변으로 이동 하여 Main 메서드의 반환 유형을 Task
또는로 변경하십시오 Task<int>
. 그렇지 않은 경우 :
- 이
async Task MainAsync
요한처럼 말했다 . - do0g와 같이
.GetAwaiter().GetResult()
기본 예외를 잡으려면 호출하십시오 . - 코리 같은 지원 취소 했다 .
- 두 번째
CTRL+C
는 프로세스를 즉시 종료해야합니다. ( binki 감사 합니다 !) - 처리
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.
}
Main에서 비동기 적으로 작업을 호출하려면 다음을 사용하십시오.
.NET 4.5 용 Task.Run ()
.NET 4.0 용 Task.Factory.StartNew () (비동기 및 대기 키워드에 Microsoft.Bcl.Async 라이브러리가 필요할 수 있음)
세부 사항 : http://blogs.msdn.com/b/pfxteam/archive/2011/10/24/10229468.aspx
Main에서 GetList 호출을 다음과 같이 변경하십시오.
Task.Run(() => bs.GetList());
C # 5 CTP가 도입되었을 때 Main으로 표시 할 수async
는 있지만 일반적으로 그렇게하는 것은 좋지 않습니다. VS 2013의 출시로 인해 이것이 변경되었다고 생각합니다.
다른 포 그라운드 스레드를 시작하지 않은 Main
경우 백그라운드 작업이 시작된 경우에도 프로그램이 완료되면 종료됩니다 .
당신은 정말로 무엇을 하려고합니까? 당신의 것을 참고 GetList()
는 진짜 이유 별도의 레이어를 추가 있어요 - 방법은 정말 순간에 비동기 할 필요는 없습니다. 논리적으로 동일하지만 (보다 복잡합니다)
public Task<List<TvChannel>> GetList()
{
return new GetPrograms().DownloadTvChannels();
}
최신 버전의 C #-C # 7.1에서는 비동기 콘솔 앱을 만들 수 있습니다. 프로젝트에서 C # 7.1을 사용하려면 VS를 15.3 이상으로 업그레이드하고 C # 버전을 C# 7.1
또는로 변경해야 C# latest minor version
합니다. 이를 수행하려면 프로젝트 특성-> 빌드-> 고급-> 언어 버전으로 이동하십시오.
이 후 다음 코드가 작동합니다.
internal class Program
{
public static async Task Main(string[] args)
{
(...)
}
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 의 답변 을 참조하십시오 .
그리고 작업이 실행되는 스레드와 관련하여 Stephen의 답변 에 대한 Stephen의 의견 도 주목하십시오 .
간단한 또는을 사용할 수 있으며 아무런 문제가 없습니다. 그러나 두 가지 중요한 차이점이 있습니다. 1) 모든 연속이 기본 스레드가 아닌 스레드 풀에서 실행되고 2) 예외가에 래핑됩니다 .
Wait
Result
async
AggregateException
(을 처리하기 위해 예외 처리를 통합하는 방법 은 예외 처리 (작업 병렬 라이브러리) 를 참조하십시오 AggregateException
.)
마지막으로 MSDN의 Task.Delay Method (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
a delegate
를 전달하는 Task.Run
대신 다음과 같이 람다 함수를 전달할 수 있습니다.
var t = Task.Run(async () =>
{
await Task.Delay(TimeSpan.FromSeconds(1.5));
return 42;
});
현재 스레드를 재결합하려고 시도하는 대기 스택에있는 함수를 호출 할 때 정지되는 것을 방지하려면 다음을 수행해야합니다.
class Program
{
static void Main(string[] args)
{
Bootstrapper bs = new Bootstrapper();
List<TvChannel> list = Task.Run((Func<Task<List<TvChannel>>>)bs.GetList).Result;
}
}
(캐스트는 모호성을 해결하기 위해서만 필요합니다)
내 경우에는 기본 방법에서 비동기로 실행하고 싶었던 작업 목록이 있었으며 프로덕션 에서이 작업을 꽤 오랫동안 사용하고 있었고 정상적으로 작동했습니다.
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 ...
}
'Programing' 카테고리의 다른 글
기존 폴더에“git clone”하는 가장 좋은 방법은 무엇입니까? (0) | 2020.02.21 |
---|---|
iOS DeviceSupport에서 데이터를 삭제할 수 있습니까? (0) | 2020.02.21 |
언제 추상 클래스 대신 인터페이스를 사용해야합니까? (0) | 2020.02.21 |
Nginx — 루트 및 별칭과 혼동되는 정적 파일 (0) | 2020.02.21 |
버튼 텍스트가 Lollipop의 모든 캡으로 강제되는 이유는 무엇입니까? (0) | 2020.02.21 |