[c#] ASP.NET Web API에서 효과적으로 async / await 사용

async/await내 웹 API 프로젝트에서 ASP.NET 의 기능을 사용하려고합니다 . 내 웹 API 서비스의 성능에 어떤 차이가 있는지 잘 모르겠습니다. 내 응용 프로그램의 워크 플로 및 샘플 코드를 아래에서 찾으십시오.

작업 흐름 :

UI Application → Web API endpoint (controller) → Web API 서비스 레이어에서 메서드 호출 → 다른 외부 웹 서비스 호출. (여기에 DB 상호 작용 등이 있습니다.)

제어 장치:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

서비스 계층 :

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

위의 코드를 테스트하고 작동 중입니다. 그러나 그것이 올바른 사용법인지 확실하지 않습니다 async/await. 여러분의 생각을 공유 해주세요.



답변

API의 성능에 어떤 차이가 있는지 잘 모르겠습니다.

서버 측에서 비동기 코드의 주요 이점은 확장 성 입니다. 마술처럼 요청이 더 빠르게 실행되지는 않습니다. ASP.NET 에 대한async기사에서async 몇 가지 “사용해야 하는가 “고려 사항을 다룹니다 .

사용 사례 (다른 API 호출)가 비동기 코드에 적합하다고 생각합니다. “비동기”가 “더 빠름”을 의미하지는 않는다는 점을 명심하십시오. 가장 좋은 방법은 먼저 UI를 반응 형 및 비동기식으로 만드는 것입니다 . 이렇게하면 앱 약간 느려도 더 빠르게 느껴 집니다.

코드가 진행되는 한 이것은 비동기가 아닙니다.

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
  var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
  return Task.FromResult(response);
}

다음과 같은 확장 성 이점을 얻으려면 진정한 비동기 구현이 필요합니다 async.

public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

또는 (이 메서드의 논리가 실제로 통과 일 경우) :

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
{
  return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
}

이와 같이 “외부”보다 “내부”에서 작업하는 것이 더 쉽습니다. 즉, 비동기 컨트롤러 작업으로 시작하지 말고 다운 스트림 메서드를 비동기로 강제 설정하십시오. 대신 자연스러운 비동기 작업 (외부 API 호출, 데이터베이스 쿼리 등)을 식별하고 가장 낮은 수준에서 먼저 비동기 작업을 만듭니다 ( Service.ProcessAsync). 그런 다음 async마지막 단계로 컨트롤러 작업을 비동기식으로 만들어서 조금씩 흐르게하십시오.

Task.Run이 시나리오 에서는 어떤 상황에서도 사용해서는 안됩니다 .


답변

정확하지만 유용하지 않을 수 있습니다.

기다릴 것이 없으므로 (비동기 적으로 작동 할 수있는 차단 API에 대한 호출이 없음) 비동기 작업 (오버 헤드가 있음)을 추적하는 구조를 설정 한 다음 해당 기능을 사용하지 않습니다.

예를 들어 서비스 계층이 비동기 호출을 지원하는 Entity Framework로 DB 작업을 수행하는 경우 :

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    using (db = myDBContext.Get()) {
      var list = await db.Countries.Where(condition).ToListAsync();

       return list;
    }
}

db가 쿼리되는 동안 작업자 스레드가 다른 작업을 수행하도록 허용하여 다른 요청을 처리 할 수 ​​있습니다.

Await는 완전히 내려 가야하는 경향이 있습니다. 기존 시스템에 개조하는 것은 매우 어렵습니다.


답변

동기 메서드 를 실행하는 동안 요청 스레드가 차단되므로 비동기 / 대기 효과를 효과적으로 활용하지 못합니다.ReturnAllCountries()

요청을 처리하기 위해 할당 된 스레드는 ReturnAllCountries()작동 하는 동안 유휴 상태로 대기 합니다.

ReturnAllCountries()비동기식으로 구현할 수 있다면 확장 성 이점을 얻을 수 있습니다. 스레드 ReturnAllCountries()가 실행 되는 동안 다른 요청을 처리하기 위해 .NET 스레드 풀로 다시 해제 될 수 있기 때문 입니다. 이렇게하면 스레드를보다 효율적으로 활용하여 서비스의 처리량을 높일 수 있습니다.


답변

서비스 계층을 다음과 같이 변경합니다.

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    return Task.Run(() =>
    {
        return _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
    }
}

당신이 그것을 가지고 있기 때문에, 당신은 여전히 ​​당신의 _service.Process호출을 동 기적으로 실행 하고 있으며 그것을 기다리는 것으로부터 거의 또는 전혀 이익을 얻지 못합니다.

이 접근 방식을 사용하면 잠재적으로 느린 호출을에서 래핑하고 Task시작하고 대기 하도록 반환합니다. 이제 Task.


답변