C #에서 ValueTask를 사용하는 방법

비동기 프로그래밍은 현재 꽤 오랫동안 사용되어 왔습니다. 최근에는 async 및 await 키워드를 도입하여 더욱 강력 해졌습니다. 비동기 프로그래밍을 활용하여 애플리케이션의 응답 성과 처리량을 높일 수 있습니다.

C #에서 권장되는 비동기 메서드의 반환 형식은 Task입니다. 값을 반환하는 비동기 메서드를 작성하려면 Task를 반환해야합니다. 이벤트 핸들러를 작성하려면 대신 void를 반환 할 수 있습니다. C # 7.0까지 비동기 메서드는 Task, Task 또는 void를 반환 할 수 있습니다. C # 7.0부터 비동기 메서드는 ValueTask (System.Threading.Tasks.Extensions 패키지의 일부로 사용 가능) 또는 ValueTask를 반환 할 수도 있습니다. 이 문서에서는 C #에서 ValueTask를 사용하는 방법에 대한 설명을 제공합니다.

이 문서에 제공된 코드 예제를 사용하려면 시스템에 Visual Studio 2019가 설치되어 있어야합니다. 아직 복사본이없는 경우 여기에서 Visual Studio 2019를 다운로드 할 수 있습니다.

Visual Studio에서 .NET Core 콘솔 애플리케이션 프로젝트 만들기

먼저 Visual Studio에서 .NET Core 콘솔 애플리케이션 프로젝트를 만들어 보겠습니다. Visual Studio 2019가 시스템에 설치되어 있다고 가정하고 아래에 설명 된 단계에 따라 Visual Studio에서 새 .NET Core 콘솔 애플리케이션 프로젝트를 만듭니다.

  1. Visual Studio IDE를 시작합니다.
  2. "새 프로젝트 만들기"를 클릭합니다.
  3. "새 프로젝트 만들기"창의 표시된 템플릿 목록에서 "콘솔 앱 (.NET Core)"을 선택합니다.
  4. 다음을 클릭하십시오.
  5. 다음에 표시된 "새 프로젝트 구성"창에서 새 프로젝트의 이름과 위치를 지정합니다.
  6. 생성을 클릭합니다.

그러면 Visual Studio 2019에 새로운 .NET Core 콘솔 애플리케이션 프로젝트가 생성됩니다.이 프로젝트를 사용하여이 문서의 후속 섹션에서 ValueTask 사용을 설명합니다.

ValueTask를 사용해야하는 이유는 무엇입니까?

작업은 일부 작업의 상태, 즉 작업 완료, 취소 여부 등을 나타냅니다. 비동기 메서드는 Task 또는 ValueTask를 반환 할 수 있습니다.

이제 Task는 참조 형식이므로 비동기 메서드에서 Task 개체를 반환하는 것은 메서드가 호출 될 때마다 관리되는 힙에 개체를 할당하는 것을 의미합니다. 따라서 Task 사용시주의 할 사항은 메서드에서 Task 개체를 반환 할 때마다 관리되는 힙에 메모리를 할당해야한다는 것입니다. 메서드에서 수행중인 작업의 결과를 즉시 사용할 수 있거나 동기식으로 완료하면이 할당이 필요하지 않으므로 비용이 많이 듭니다.

ValueTask가 구출되는 정확한 위치입니다. ValueTask는 두 가지 주요 이점을 제공합니다. 첫째, ValueTask는 힙 할당이 필요하지 않기 때문에 성능을 향상시키고 둘째, 구현하기 쉽고 유연합니다. 결과를 즉시 사용할 수있을 때 비동기 메서드에서 Task 대신 ValueTask를 반환하면 여기서 "T"는 구조를 나타내고 C #의 구조체는 "T"와 달리 값 형식이므로 불필요한 할당 오버 헤드를 피할 수 있습니다. 클래스를 나타내는 Task에서).

Task 및 ValueTask는 C #에서 두 가지 기본 "대기 가능한"유형을 나타냅니다. ValueTask에서는 차단할 수 없습니다. 차단해야하는 경우 AsTask 메서드를 사용하여 ValueTask를 Task로 변환 한 다음 해당 참조 Task 개체를 차단해야합니다.

또한 각 ValueTask는 한 번만 사용할 수 있습니다. 여기서 "소비"라는 단어는 ValueTask가 작업이 완료 될 때까지 비동기 적으로 대기 (대기)하거나 AsTask를 활용하여 ValueTask를 Task로 변환 할 수 있음을 의미합니다. 그러나 ValueTask는 한 번만 소비되어야하며 그 후에는 ValueTask를 무시해야합니다.

C #의 ValueTask 예제

Task를 반환하는 비동기 메서드가 있다고 가정합니다. Task.FromResult를 활용하여 아래 제공된 코드 스 니펫에 표시된대로 Task 개체를 만들 수 있습니다.

공용 태스크 GetCustomerIdAsync ()

{

    return Task.FromResult (1);

}

위의 코드 조각은 전체 비동기 상태 시스템 마법을 생성하지 않지만 관리되는 힙에 Task 개체를 할당합니다. 이 할당을 피하려면 아래 제공된 코드 스 니펫에 표시된대로 ValueTask를 대신 활용할 수 있습니다.

공용 ValueTask GetCustomerIdAsync ()

{

    return new ValueTask (1);

}

다음 코드 스 니펫은 ValueTask의 동기 구현을 보여줍니다.

 공용 인터페이스 IRepository

    {

        ValueTask GetData ();

    }

Repository 클래스는 IRepository 인터페이스를 확장하고 아래와 같이 해당 메서드를 구현합니다.

    공용 클래스 저장소 : IRepository

    {

        공용 ValueTask GetData ()

        {

            var 값 = default (T);

            새로운 ValueTask (값);

        }

    }

Main 메서드에서 GetData 메서드를 호출하는 방법은 다음과 같습니다.

static void Main (string [] args)

        {

            IRepository 저장소 = new Repository ();

            var result = repository.GetData ();

            if (result.IsCompleted)

                 Console.WriteLine ( "작업 완료 ...");

            그밖에

                Console.WriteLine ( "작업이 완료되지 않았습니다 ...");

            Console.ReadKey ();

        }

이제 저장소에 또 다른 메서드를 추가해 보겠습니다. 이번에는 GetDataAsync라는 비동기 메서드입니다. 수정 된 IRepository 인터페이스는 다음과 같습니다.

공용 인터페이스 IRepository

    {

        ValueTask GetData ();

        ValueTask GetDataAsync ();

    }

GetDataAsync 메서드는 아래 제공된 코드 조각에 표시된대로 Repository 클래스에 의해 구현됩니다.

    공용 클래스 저장소 : IRepository

    {

        공용 ValueTask GetData ()

        {

            var 값 = default (T);

            새로운 ValueTask (값);

        }

        공용 비동기 ValueTask GetDataAsync ()

        {

            var 값 = default (T);

            await Task.Delay (100);

            반환 값;

        }

    }

C #에서 ValueTask를 언제 사용해야합니까?

ValueTask가 제공하는 이점에도 불구하고 Task 대신 ValueTask를 사용하는 데는 특정 절충안이 있습니다. ValueTask는 두 개의 필드가있는 값 유형 인 반면 Task는 단일 필드가있는 참조 유형입니다. 따라서 ValueTask를 사용한다는 것은 메서드 호출이 하나 대신 두 개의 데이터 필드를 반환하므로 더 많은 데이터로 작업하는 것을 의미합니다. 또한 ValueTask를 반환하는 메서드를 기다리는 경우 해당 비동기 메서드에 대한 상태 머신도 더 커집니다. 태스크의 경우 단일 참조 대신 두 필드를 포함하는 구조체를 수용해야하기 때문입니다.

또한 비동기 메서드의 소비자가 Task.WhenAll 또는 Task.WhenAny를 사용하는 경우 비동기 메서드에서 ValueTask를 반환 형식으로 사용하면 비용이 많이들 수 있습니다. 이는 AsTask 메서드를 사용하여 ValueTask를 Task로 변환해야하기 때문입니다.이 경우 캐시 된 Task가 처음에 사용 된 경우 쉽게 피할 수있는 할당이 발생합니다.

여기에 엄지의 법칙이 있습니다. 작업이 즉시 완료되지 않는 경우와 같이 항상 비동기적인 코드가있는 경우 Task를 사용합니다. 비동기 작업의 결과가 이미 사용 가능하거나 이미 캐시 된 결과가있는 경우 ValueTask를 활용하십시오. 어느 쪽이든 ValueTask를 고려하기 전에 필요한 성능 분석을 수행해야합니다.

C #에서 더 많은 작업을 수행하는 방법 :

  • C에서 불변성을 사용하는 방법
  • C #에서 const, readonly 및 static을 사용하는 방법
  • C #에서 데이터 주석을 사용하는 방법
  • C # 8에서 GUID를 사용하는 방법
  • C #에서 추상 클래스와 인터페이스를 사용하는 경우
  • C #에서 AutoMapper를 사용하는 방법
  • C #에서 람다 식을 사용하는 방법
  • C #에서 Action, Func 및 Predicate 대리자를 사용하는 방법
  • C #에서 대리자를 사용하는 방법
  • C #에서 간단한 로거를 구현하는 방법
  • C #에서 특성을 사용하는 방법
  • C #에서 log4net으로 작업하는 방법
  • C #에서 리포지토리 디자인 패턴을 구현하는 방법
  • C #에서 리플렉션을 사용하는 방법
  • C #에서 filesystemwatcher로 작업하는 방법
  • C #에서 지연 초기화를 수행하는 방법
  • C #에서 MSMQ를 사용하는 방법
  • C #에서 확장 메서드를 사용하는 방법
  • C #에서 람다 식 사용 방법
  • C #에서 volatile 키워드를 사용하는 경우
  • C #에서 yield 키워드를 사용하는 방법
  • C #에서 다형성을 구현하는 방법
  • C #에서 자신 만의 작업 스케줄러를 빌드하는 방법
  • C #에서 RabbitMQ로 작업하는 방법
  • C #에서 튜플을 사용하는 방법
  • C #에서 가상 및 추상 메서드 탐색
  • C #에서 Dapper ORM을 사용하는 방법
  • C #에서 플라이 웨이트 디자인 패턴을 사용하는 방법