비동기 메서드를 동 기적으로 호출


231

나는이 async방법을 :

public async Task<string> GenerateCodeAsync()
{
    string code = await GenerateCodeService.GenerateCodeAsync();
    return code;
}

이 메소드를 동기 메소드에서 호출해야합니다.

GenerateCodeAsync이것이 동 기적으로 작동하기 위해 메소드 를 복제하지 않고 어떻게 할 수 있습니까?

최신 정보

그러나 합리적인 해결책을 찾지 못했습니다.

그러나 HttpClient이미이 패턴을 구현하고 있음을 알았습니다.

using (HttpClient client = new HttpClient())
{
    // async
    HttpResponseMessage responseAsync = await client.GetAsync(url);

    // sync
    HttpResponseMessage responseSync = client.GetAsync(url).Result;
}


1
간단한 코드를 작성하는 것보다 asp.net이 훨씬 쉽게 처리 할 수 ​​있다고 생각하면서 더 간단한 솔루션을 원했습니다.
Catalin

왜 비동기 코드를 받아들이지 않는가? 이상적으로는 더 많은 비동기 코드를 원할 것입니다.
Paulo Morgado

53
[비동기 코드 만 수용하는 이유는 무엇입니까?] Ha, 프로젝트의 많은 부분이 변환 될 때이 솔루션이 필요한 비동기 코드를 수용하고 있기 때문일 수 있습니다. 하루에 로마를 재건 할 수 없습니다.
Nicholas Petersen

1
@NicholasPetersen 때로는 타사 라이브러리 에서이 작업을 수행 할 수 있습니다. FluentValidation에서 WithMessage 메소드로 동적 메시지 작성 예제. 라이브러리 디자인으로 인해 비동기 API가 없습니다. WithMessage 오버로드가 정적입니다. WithMessage에 동적 인수를 전달하는 다른 방법은 이상합니다.
H.Wojtowicz

답변:


278

Result작업 의 속성에 액세스 할 수 있으며 결과를 사용할 수있을 때까지 스레드가 차단됩니다.

string code = GenerateCodeAsync().Result;

참고 : 경우에 따라 교착 상태가 발생할 수 있습니다 Result. 메인 스레드를 차단하면 나머지 비동기 코드가 실행되지 않습니다. 이런 일이 발생하지 않도록하려면 다음과 같은 옵션이 있습니다.

이것은 하지 않습니다 그냥 아무 생각없이 추가해야한다는 것을 의미 .ConfigureAwait(false)모든 비동기 호출 후! 사용시기 및시기에 대한 자세한 분석 .ConfigureAwait(false)은 다음 블로그 게시물을 참조하십시오.


33
호출이 경우 result교착 상태 위험, 다음 경우 입니다 그것은 결과를 얻을 안전? 모든 비동기 호출에는 Task.Run또는 ConfigureAwait(false)?가 필요 합니까?
Robert Harvey

4
ASP.NET에는 "주 스레드"가 없지만 (GUI 앱과는 달리) 비동기 연속을 직렬화 하는 방법으로 인해 교착 상태가 여전히 발생할 수 있습니다 AspNetSynchronizationContext.Post.Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action)); _lastScheduledTask = newTask;
noseratio

4
@RobertHarvey : 차단하고있는 비동기 메소드의 구현을 제어 할 수 없다면, Task.Run보안을 유지하기 위해 랩핑해야 합니다. 또는 WithNoContext중복 스레드 전환을 줄이는 것과 같은 것을 사용하십시오 .
noseratio

10
참고 : .Result호출자가 스레드 풀 자체에 있으면 호출 은 여전히 ​​교착 상태가 될 수 있습니다. 스레드 풀의 크기가 32 및 32 크기 인 작업이 실행 중이고 Wait()/Result대기중인 스레드 중 하나에서 실행하려는 아직 예약되지 않은 33 번째 작업을 기다리는 시나리오를 작성하십시오 .
Warty

55

대기자 ( GetAwaiter())를 확보하고 비동기 타스크 ( GetResult()) 의 완료 대기를 종료해야합니다 .

string code = GenerateCodeAsync().GetAwaiter().GetResult();

37
이 솔루션을 사용하여 교착 상태가 발생했습니다. 경고 받다.
Oliver

6
MSDNTask.GetAwaiter :이 방법은 응용 프로그램 코드에서 사용하기보다는 컴파일러에서 사용하기위한 것입니다.
foka

'Switch To'또는 'Retry'… 버튼과 함께 오류 대화 상자 팝업이 나타납니다 (내 의지에 대한). 그러나 호출은 실제로 실행되고 적절한 응답으로 반환됩니다.
조나단 한센

30

델리게이트, 람다 표현을 사용 하여이 작업을 수행 할 수 있어야합니다

private void button2_Click(object sender, EventArgs e)
    {

        label1.Text = "waiting....";

        Task<string> sCode = Task.Run(async () =>
        {
            string msg =await GenerateCodeAsync();
            return msg;
        });

        label1.Text += sCode.Result;

    }

    private Task<string> GenerateCodeAsync()
    {
        return Task.Run<string>(() => GenerateCode());
    }

    private string GenerateCode()
    {
        Thread.Sleep(2000);
        return "I m back" ;
    }

이 스 니펫은 컴파일되지 않습니다. Task.Run의 반환 유형은 Task입니다. 자세한 설명 은이 MSDN 블로그 를 참조하십시오 .
Appetere

5
지적 해 주셔서 감사합니다. 그렇습니다. 작업 유형을 반환합니다. "string sCode"를 Task <string> 또는 var sCode로 바꾸면 해결됩니다. 전체 컴파일 코드를 쉽게 추가 할 수 있습니다.
Faiyaz

20

이 메소드를 동기식 메소드에서 호출해야합니다.

다른 대답에서 알 수 있듯이 GenerateCodeAsync().Result또는로 가능 GenerateCodeAsync().Wait()합니다. GenerateCodeAsync완료 될 때까지 현재 스레드를 차단합니다 .

그러나 귀하의 질문에 태그가 지정되었습니다 , 그리고 당신은 또한 의견을 남겼습니다 :

asp.net이 많은 코드 줄을 작성하는 것보다 훨씬 쉽게 처리 할 수 ​​있다고 생각하면서 더 간단한 솔루션을 원했습니다.

요점은 ASP.NET의 비동기 메서드를 차단해서는 안된다는 것 입니다. 이렇게하면 웹 앱의 확장 성이 줄어들고 교착 상태가 발생할 수 있습니다 ( await내부 에 연속 GenerateCodeAsync이 게시 될 때 AspNetSynchronizationContext). 사용 Task.Run(...).Result이 특정 HTTP 요청을 처리하기 위해 하나 이상의 스레드를 초래으로 풀 스레드 다음 블록에 오프로드 뭔가하면, 더 많은 확장 성에게조차 상처를합니다.

ASP.NET을 통해 직접 비동기 컨트롤러를 통해 (웹 API ASP.NET MVC와) 중 하나, 비동기 메서드에 대한 지원 내장 또는했다 AsyncManagerPageAsyncTask고전 ASP.NET에서. 사용해야합니다. 자세한 내용은 이 답변을 확인 하십시오 .


SaveChanges()메소드를 덮어 쓰고 DbContext있습니다. 여기서 비동기 메소드를 호출합니다. 불행히도 비동기 컨트롤러는이 상황에서 도움이되지 않습니다.
Catalin

3
@RaraituL, 일반적으로 비동기 코드와 동기화 코드를 혼합하지 않고 다른 모델을 선택하십시오. 당신은 모두를 구현할 수 SaveChangesAsyncSaveChanges단지 그들이 같은 ASP.NET 프로젝트에 모두 전화를받을하지 않습니다 있는지 확인하십시오.
noseratio

4
.NET MVC예를 들어 모든 필터가 비동기 코드를 지원하는 것은 아니므로 모든 방법을 IAuthorizationFilter사용할 수는 없습니다async
Catalin

3
비현실적인 목표 인 @Noseratio. 비동기식 코드와 동기식 코드를 가진 라이브러리가 너무 많으며 하나의 모델 만 사용할 수없는 상황이 있습니다. 예를 들어 MVC ActionFilters는 비동기 코드를 지원하지 않습니다.
Justin Skiles

9
@ Noserato, 문제는 동기에서 비동기 메소드를 호출하는 것입니다. 언젠가 구현하는 API를 변경할 수 없습니다. 일부 타사 프레임 워크 "A"(프레임 워크를 비동기 방식으로 다시 작성할 수 없음)에서 일부 동기식 인터페이스를 구현하지만 구현에 사용하려는 타사 라이브러리 "B"는 비동기식이라고 가정합니다. 또한 결과 제품은 라이브러리이며 ASP.NET 등을 포함한 모든 곳에서 사용할 수 있습니다.
dimzon

19

Microsoft Identity에는 비동기 메서드를 동 기적으로 호출하는 확장 메서드가 있습니다. 예를 들어 GenerateUserIdentityAsync () 메서드와 동일한 CreateIdentity ()가 있습니다

UserManagerExtensions.CreateIdentity ()를 보면 다음과 같습니다.

 public static ClaimsIdentity CreateIdentity<TUser, TKey>(this UserManager<TUser, TKey> manager, TUser user,
        string authenticationType)
        where TKey : IEquatable<TKey>
        where TUser : class, IUser<TKey>
    {
        if (manager == null)
        {
            throw new ArgumentNullException("manager");
        }
        return AsyncHelper.RunSync(() => manager.CreateIdentityAsync(user, authenticationType));
    }

이제 AsyncHelper.RunSync의 기능을 살펴 보겠습니다.

  public static TResult RunSync<TResult>(Func<Task<TResult>> func)
    {
        var cultureUi = CultureInfo.CurrentUICulture;
        var culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew(() =>
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap().GetAwaiter().GetResult();
    }

따라서 이것은 비동기 메소드의 래퍼입니다. 그리고 결과에서 데이터를 읽지 마십시오. ASP에서 코드를 잠재적으로 차단합니다.

나에게는 의심스러운 또 다른 방법이 있지만, 당신도 그것을 고려할 수 있습니다

  Result r = null;

            YourAsyncMethod()
                .ContinueWith(t =>
                {
                    r = t.Result;
                })
                .Wait();

3
제안한 두 번째 방법의 문제점은 무엇이라고 생각하십니까?
David Clarke

@DavidClarke는 아마도 잠금없이 여러 스레드에서 비 휘발성 변수에 액세스하는 스레드 안전 문제 일 것입니다.
Theodor Zoulias

9

교착 상태를 방지하기 위해 Task.Run()@Heinzi가 언급 한 비동기 메소드를 동 기적으로 호출해야 할 때 항상 사용하려고 합니다.

그러나 비동기 메소드가 매개 변수를 사용하는 경우 메소드를 수정해야합니다. 예를 들어 Task.Run(GenerateCodeAsync("test")).Result오류가 발생합니다.

인수 1 : ' System.Threading.Tasks.Task<string>'에서 'System.Action'으로 변환 할 수 없습니다

대신 다음과 같이 호출 할 수 있습니다.

string code = Task.Run(() => GenerateCodeAsync("test")).Result;

6

이 스레드에 대한 대부분의 답변은 복잡하거나 교착 상태가 발생합니다.

다음 방법은 간단하며 작업이 끝날 때까지 기다렸다가 결과를 얻기 때문에 교착 상태를 피할 수 있습니다.

var task = Task.Run(() => GenerateCodeAsync()); 
task.Wait();
string code = task.Result;

또한 여기에 정확히 동일한 내용에 대해 이야기하는 MSDN 기사에 대한 참조가 있습니다. 기본 문맥 /


1

비 차단 접근 방식을 선호합니다.

            Dim aw1=GenerateCodeAsync().GetAwaiter()
            While Not aw1.IsCompleted
                Application.DoEvents()
            End While

0

글쎄, 나는이 접근법을 사용하고있다 :

    private string RunSync()
    {
        var task = Task.Run(async () => await GenerateCodeService.GenerateCodeAsync());
        if (task.IsFaulted && task.Exception != null)
        {
            throw task.Exception;
        }

        return task.Result;
    }

-1

다른 방법은 작업이 끝날 때까지 기다릴 수 있습니다.

var t = GenerateCodeService.GenerateCodeAsync();
Task.WhenAll(t);
string code = t.Result;

1
그것은 명백한 잘못입니다. WhenAll은 또한 기다리지 않는 작업을 반환합니다.
Robert Schmidt

-1

편집하다:

Task에는 Wait 메서드 인 Task.Wait ()가 있습니다.이 메서드는 "약속"이 해결 될 때까지 기다렸다가 계속 진행하므로 동 기적으로 렌더링됩니다. 예:


async Task<String> MyAsyncMethod() { ... }

String mySyncMethod() {

    return MyAsyncMethod().Wait();
}

3
친절하게 답변을 정하십시오. 어떻게 사용 되나요? 질문에 대답하는 데 구체적으로 어떤 도움이됩니까?
Scratte

-2

" RefreshList " 라는 비동기 메소드가있는 경우 아래와 같이 비동기가 아닌 메소드에서 해당 비동기 메소드를 호출 할 수 있습니다.

Task.Run(async () => { await RefreshList(); });
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.