await async 메소드에서 리턴 타입이 ValueTask 일 때, 꼭 return new ValueTask(Enum) 으로 감싸주지 않고 바로 return Enum 해도 완전히 똑같은가요?
예를 들면 아래와 같은 경우… 두 경우 모두 완전히 같은 결과인지 궁금하네요 (CLR 인터프리터 코드 보면 알 수 있겠지만 확인차…)
public async ValueTask < EnumReturnType > TestReturnValueTask()
{
return EnumReturnType.Success; // 암시적 형 변환을 기대
// return new ValueTask < EnumReturnType > (EnumReturnType.Success); // 명시적으로 할당하지 않아도 암시적 형 변환과 동일한 결과를 기대
}
실제 코드에는 중간에 db save 등과 같은 async await 메소드가 포함되어있습니다. 그래서 async 메소드 안에서 new ValueTask(value) 또는 ValueTask.FromResult(value) 대신 바로 value를 리턴해도 상관 없는지 여부가 궁금했습니다
async 메소드의 리턴 타입으로 ValueTask를 사용할 때, 일반적으로는 ValueTask.FromResult(result)를 사용하여 ValueTask 객체를 만들어 리턴합니다. 이렇게 하면 메모리 할당을 방지하고 성능을 향상시킬 수 있습니다. 하지만 만약에 메소드의 구현에서 ValueTask 객체를 사용하여 반환하지 않고, 일반 데이터 타입을 즉시 반환한다면, CLR은 내부적으로 암시적으로 ValueTask 객체로 형변환을 수행합니다.
즉, 일반 데이터 타입을 반환하는 경우 암시적 형변환이 발생하므로 명시 적으로 ValueTask를 할당할 필요는 없습니다. 수행 결과는 기본적으로 ValueTask로 형변환됩니다. 이 경우 CLR은 ValueTask.Builder를 사용하여 새로운 ValueTask 객체를 생성하지 않고 원본 Task를 사용합니다.
이러한 경우 반면에 ValueTask를 반환하는 경우, 새로운 ValueTask 객체를 할당해야합니다. 반환된 ValueTask 객체에는 스택 프레임에있는 것과 System.Threading.Tasks.ValueTaskCompletionSource 개체가 할당되며 반환값의 참조 카운트를 추적하는 데 사용됩니다.
결론적으로, ValueTask를 반환하는 경우 새로운 ValueTask 객체를 할당하여 반환하는 것이 좋습니다. 그러나 일반 데이터 타입을 반환하는 경우, 성능상의 이유로 불필요한 짓은 하지 않아도 됩니다.
public ValueTask<Person> GetById(int id)
{
return dbcontext.People.FindAsync(id);
}
다만, 내부에 동기적으로 수행해야 할 또 다른 비동기 작업이 있을 경우에는 반드시 Async 를 써야 합니다.
public async ValueTask<Student?> GetByPersonId(int personId)
{
var person = await dbcontext.People.FindAsync(id); // 먼저 수행해야 하는 비동기 작업
if(person == null)
return null;
return (await dbcontext.Students.Where(x => x.Name == person.Name))?.FirstOrDefault();
}
아래와 같은 async 메서드를 Data 레이어에서 정의하면, 이를 소비하는 다른 레이어에서도 async/await 을 할 수 밖에 없는데, 이는 기다리기만 하는 상태 머신만 계속 양산하는 결과를 낳습니다.
따라서, async 는 가급적 최종 소비 레이어, 예를 들면, UI 레이어에서 수행되어야, 딱 필요한 만큼의 상태 머신을 소비합니다.
public async ValueTask<Student?> GetByPersonId(int personId)
{
var person = await dbcontext.People.FindAsync(id); // 먼저 수행해야 하는 비동기 작업
if(person == null)
return null;
return (await dbcontext.Students.Where(x => x.Name == person.Name))?.FirstOrDefault();
}