비동기 코드에서 비동기 메서드 호출
.NET 3.5에서 빌드 된 API 표면이있는 라이브러리를 업데이트하는 중입니다. 결과적으로 모든 메서드는 동기식입니다. 모든 호출자가 변경되어야하기 때문에 API를 변경할 수 없습니다 (즉, 반환 값을 Task로 변환). 그래서 동기 방식으로 비동기 메서드를 가장 잘 호출하는 방법이 남았습니다. 이것은 ASP.NET 4, ASP.NET Core 및 .NET / .NET Core 콘솔 애플리케이션의 컨텍스트에 있습니다.
충분히 명확하지 않을 수 있습니다. 상황은 비동기 인식이없는 기존 코드가 있고 비동기 메서드 만 지원하는 System.Net.Http 및 AWS SDK와 같은 새로운 라이브러리를 사용하고 싶습니다. 따라서 격차를 해소하고 동 기적으로 호출 할 수 있지만 다른 곳에서 비동기 메서드를 호출 할 수있는 코드를 가질 수 있어야합니다.
나는 많은 독서를했고, 이것에 대한 질문과 답변을 많이 받았습니다.
비동기 작업을 동기식으로 기다리고 있으며 Wait ()가 여기에서 프로그램을 정지시키는 이유
비동기 Task <T> 메서드를 어떻게 동 기적으로 실행합니까?
C #의 동기 메서드에서 비동기 메서드를 호출하는 방법은 무엇입니까?
문제는 대부분의 답변이 다르다는 것입니다! 내가 본 가장 일반적인 접근 방식은 .Result를 사용하는 것이지만 이것은 교착 상태가 될 수 있습니다. 나는 다음을 모두 시도해 보았지만 작동하지만 교착 상태를 피하고 성능이 좋으며 런타임과 잘 어울리는 최선의 방법이 무엇인지 모르겠습니다 (작업 스케줄러, 작업 생성 옵션 등을 존중하는 측면에서) ). 확실한 답이 있습니까? 가장 좋은 방법은 무엇입니까?
private static T taskSyncRunner<T>(Func<Task<T>> task)
{
T result;
// approach 1
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 2
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// approach 3
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// approach 4
result = Task.Run(task).Result;
// approach 5
result = Task.Run(task).GetAwaiter().GetResult();
// approach 6
var t = task();
t.RunSynchronously();
result = t.Result;
// approach 7
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
// approach 8?
return result;
}
그래서 동기식으로 비동기 메서드를 가장 잘 호출하는 방법이 남았습니다.
첫째, 이것은 괜찮은 일입니다. 이것은 Stack Overflow에서이 사실을 구체적인 사례에 관계없이 총괄적인 진술로 악마의 행동으로 지적하는 것이 일반적이기 때문에 이것을 언급하고 있습니다.
정확성을 위해 비동기식 일 필요는 없습니다 . 무언가를 동기화하기 위해 비동기로 차단하는 것은 중요하거나 전혀 관련이 없을 수있는 성능 비용이 있습니다. 구체적인 경우에 따라 다릅니다.
교착 상태는 동일한 단일 스레드 동기화 컨텍스트를 동시에 입력하려고하는 두 스레드에서 발생합니다. 이를 방지하는 모든 기술은 차단으로 인한 교착 상태를 안정적으로 방지합니다.
여기, .ConfigureAwait(false)
당신이 기다리고 있지 않기 때문에 당신의 모든 전화 는 무의미합니다.
RunSynchronously
모든 작업이 그렇게 처리 될 수있는 것은 아니기 때문에 사용할 수 없습니다.
.GetAwaiter().GetResult()
예외 전파 동작을 Result/Wait()
모방한다는 점에서 다릅니다 await
. 원하는지 여부를 결정해야합니다. (그러니 그 행동이 무엇인지 조사하십시오. 여기서 반복 할 필요가 없습니다.)
그 외에도 이러한 모든 접근 방식의 성능은 비슷합니다. 그들은 OS 이벤트를 어떤 식 으로든 할당하고 차단합니다. 그것은 비싼 부분입니다. 어떤 접근 방식이 절대적으로 저렴한 지 모르겠습니다.
나는 개인적으로 Task.Run(() => DoSomethingAsync()).Wait();
패턴이 교착 상태를 피하고 단순하며 숨길 수있는 일부 예외를 숨기지 않기 때문에 좋아합니다 GetResult()
. 그러나 GetResult()
이것으로도 사용할 수 있습니다 .
.NET 3.5에서 빌드 된 API 표면이있는 라이브러리를 업데이트하는 중입니다. 결과적으로 모든 메서드는 동기식입니다. 모든 호출자가 변경되어야하기 때문에 API를 변경할 수 없습니다 (즉, 반환 값을 Task로 변환). 그래서 동기 방식으로 비동기 메서드를 가장 잘 호출하는 방법이 남았습니다.
비동기식 동기화 방지 패턴을 수행하는 보편적 인 "최상의"방법은 없습니다. 각각의 단점이있는 다양한 해킹 만 있습니다.
내가 권장하는 것은 이전 동기 API를 유지 한 다음 그와 함께 비동기 API를 도입하는 것입니다. Brownfield Async에 대한 MSDN 기사에 설명 된대로 "부울 인수 해킹"을 사용하여이 작업을 수행 할 수 있습니다 .
먼저, 예제에서 각 접근 방식의 문제점에 대한 간략한 설명 :
ConfigureAwait
이있는 경우에만 의미가 있습니다await
. 그렇지 않으면 아무것도하지 않습니다.Result
예외를 래핑합니다AggregateException
. 차단해야하는 경우GetAwaiter().GetResult()
대신 사용하십시오.Task.Run
스레드 풀 스레드에서 코드를 실행합니다 (분명히). 코드 가 스레드 풀 스레드에서 실행될 수있는 경우에만 괜찮습니다 .RunSynchronously
동적 작업 기반 병렬 처리를 수행 할 때 극히 드문 상황에서 사용되는 고급 API입니다. 당신은 전혀 그 시나리오에 있지 않습니다.Task.WaitAll
단일 작업은Wait()
.async () => await x
을 (를) 말하는 비효율적 인 방법입니다() => x
.- 현재 스레드에서 시작된 작업을 차단 하면 교착 상태가 발생할 수 있습니다 .
분석은 다음과 같습니다.
// Problems (1), (3), (6)
result = Task.Run(async () => await task()).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (3)
result = Task.Run(task).ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (1), (7)
result = task().ConfigureAwait(false).GetAwaiter().GetResult();
// Problems (2), (3)
result = Task.Run(task).Result;
// Problems (3)
result = Task.Run(task).GetAwaiter().GetResult();
// Problems (2), (4)
var t = task();
t.RunSynchronously();
result = t.Result;
// Problems (2), (5)
var t1 = task();
Task.WaitAll(t1);
result = t1.Result;
이러한 접근 방식 대신 기존의 작동하는 동기 코드 가 있으므로 새로운 자연 비동기 코드와 함께 사용해야합니다. 예를 들어 기존 코드가 다음을 사용한 경우 WebClient
:
public string Get()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
비동기 API를 추가하려면 다음과 같이합니다.
private async Task<string> GetCoreAsync(bool sync)
{
using (var client = new WebClient())
{
return sync ?
client.DownloadString(...) :
await client.DownloadStringTaskAsync(...);
}
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
또는 어떤 이유로 사용해야 하는 경우 HttpClient
:
private string GetCoreSync()
{
using (var client = new WebClient())
return client.DownloadString(...);
}
private static HttpClient HttpClient { get; } = ...;
private async Task<string> GetCoreAsync(bool sync)
{
return sync ?
GetCoreSync() :
await HttpClient.GetString(...);
}
public string Get() => GetCoreAsync(sync: true).GetAwaiter().GetResult();
public Task<string> GetAsync() => GetCoreAsync(sync: false);
With this approach, your logic would go into the Core
methods, which may be run synchronously or asynchronously (as determined by the sync
parameter). If sync
is true
, then the core methods must return an already-completed task. For implemenation, use synchronous APIs to run synchronously, and use asynchronous APIs to run asynchronously.
Eventually, I recommend deprecating the synchronous APIs.
참고URL : https://stackoverflow.com/questions/40324300/calling-async-methods-from-non-async-code
'code' 카테고리의 다른 글
BroadcastReceiver 대 WakefulBroadcastReceiver (0) | 2020.12.01 |
---|---|
Ansible : sudo 권한이있는 사용자 생성 (0) | 2020.12.01 |
HTML에서 목록에 대한 캡션, 제목 또는 레이블을 의미 론적으로 제공하는 방법 (0) | 2020.12.01 |
삼성 갤럭시 탭 에뮬레이트 (0) | 2020.12.01 |
Qt의 기본 사용자 브라우저에서 링크를 여는 방법은 무엇입니까? (0) | 2020.12.01 |