인터페이스가 Task를 반환해야하는 경우 작동하지 않는 구현을 수행하는 가장 좋은 방법은 무엇입니까?


435

아래 코드에서 인터페이스로 인해 클래스 LazyBar는 메서드에서 작업을 반환해야합니다 (인수를 변경할 수 없음). LazyBar신속하고 동 기적으로 실행된다는 점에서 구현이 특이한 경우 -메소드에서 비 작동 태스크를 리턴하는 가장 좋은 방법은 무엇입니까?

Task.Delay(0)아래에 갔지만 함수가 많이 호출되면 성능에 부작용이 있는지 알고 싶습니다 (인수를 위해 초당 수백 번 말하십시오).

  • 이 구문 설탕은 큰 것으로 풀리나요?
  • 내 응용 프로그램의 스레드 풀이 막히기 시작합니까?
  • 컴파일러 식칼은 Delay(0)다르게 처리하기에 충분 합니까?
  • 다를까요 return Task.Run(() => { });?

더 좋은 방법이 있습니까?

using System.Threading.Tasks;

namespace MyAsyncTest
{
    internal interface IFooFace
    {
        Task WillBeLongRunningAsyncInTheMajorityOfImplementations();
    }

    /// <summary>
    /// An implementation, that unlike most cases, will not have a long-running
    /// operation in 'WillBeLongRunningAsyncInTheMajorityOfImplementations'
    /// </summary>
    internal class LazyBar : IFooFace
    {
        #region IFooFace Members

        public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
        {
            // First, do something really quick
            var x = 1;

            // Can't return 'null' here! Does 'Task.Delay(0)' have any performance considerations?
            // Is it a real no-op, or if I call this a lot, will it adversely affect the
            // underlying thread-pool? Better way?
            return Task.Delay(0);

            // Any different?
            // return Task.Run(() => { });

            // If my task returned something, I would do:
            // return Task.FromResult<int>(12345);
        }

        #endregion
    }

    internal class Program
    {
        private static void Main(string[] args)
        {
            Test();
        }

        private static async void Test()
        {
            IFooFace foo = FactoryCreate();
            await foo.WillBeLongRunningAsyncInTheMajorityOfImplementations();
            return;
        }

        private static IFooFace FactoryCreate()
        {
            return new LazyBar();
        }
    }
}


8
개인적으로 나는 함께 갈 것입니다 Task.FromResult<object>(null).
코드 InChaos

답변:


625

no-op 표현식으로 작성하는 것보다 Task.FromResult(0)또는 사용하면 Task.FromResult<object>(null)오버 헤드가 줄어 듭니다 Task. Task미리 결정된 결과로을 만들 때 예약 오버 헤드가 없습니다.


오늘은 Task.CompletedTask 를 사용 하여이 작업 을 수행하는 것이 좋습니다 .


5
그리고 github.com/StephenCleary/AsyncEx 를 사용 하는 경우 완료된 작업과 함께 유용한 다른 작업 (0 int, true / false, Default <T> ())을 제공하는 TaskConstants 클래스를 제공합니다
quentin-starin

5
return default(YourReturnType);
Legends

8
@Legends 직접 작업을 만드는 데에는 효과가 없습니다.
Reed Copsey

18
확실 Task.CompletedTask하지 않지만 트릭을 수행 할 수 있습니다! (그러나 .net 4.6 필요)
Peter

1
질문 : Task.FromResult<TResult>를 반환하는 (유형 매개 변수 없음) Task<TResult>의 반환 유형을 어떻게 만족 Task시키는가?
rory.ap

187

를 사용하는 것에 대한 Reed Copsey의 답변 에 추가하기 Task.FromResult위해 완료된 작업의 모든 인스턴스가 동일하기 때문에 이미 완료된 작업을 캐시하면 성능을 훨씬 향상시킬 수 있습니다.

public static class TaskExtensions
{
    public static readonly Task CompletedTask = Task.FromResult(false);
}

함께 TaskExtensions.CompletedTask사용하면 전체 응용 프로그램 도메인 전체에 동일한 인스턴스를 사용할 수 있습니다.


닷넷 프레임 워크 (V4.6)의 최신 버전은 추가 단지 그와 Task.CompletedTask정적 속성

Task completedTask = Task.CompletedTask;

내가해야합니까 반환 을 또는 기다리고 거기에?
Pixar

@Pixar 무슨 뜻인가요? 두 가지를 모두 수행 할 수 있지만 기다리면 동 기적으로 계속 진행됩니다.
i3arnon

죄송합니다. 컨텍스트를 언급해야했습니다.) 이제 알 수 public Task WillBeLongRunningAsyncInTheMajorityOfImplementations()있듯이 public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations(). 그래서 우리는 return CompletedTask;또는 을 할 수 있습니다 await CompletedTask;. 더 바람직하거나 더 효율적일 수있는 것은 무엇입니까?
Pixar

3
@ Pixa는 명확하지 않습니다. "비동기 화가 더 효율적일 것"이라는 의미였습니다. 메소드를 비동기로 만들면 컴파일러가 메소드를 상태 머신으로 변환하도록 지시합니다. 또한 전화를 걸 때마다 새 작업이 생성됩니다. 이미 완료된 작업을 반환하면 더 명확하고 성능이 향상됩니다.
i3arnon

3
@Asad는 할당을 줄이고 GC 시간을 줄입니다. 완료된 작업이 필요할 때마다 새 메모리를 할당하고 작업 인스턴스를 구성하는 대신이 작업을 한 번만 수행하십시오.
i3arnon

38

Task.Delay(0)허용 된 답변에서와 같이 그것이 완성 된 캐시 된 사본이므로 좋은 접근 방식이었습니다 Task.

4.6 현재 Task.CompletedTask목적에 따라 더 분명한 것이 있지만 Task.Delay(0)여전히 단일 캐시 인스턴스를 반환 할 뿐만 아니라 동일한 단일 캐시 인스턴스를 반환합니다Task.CompletedTask .

의 사용도의 캐시 특성은 일정하게 유지 보장,하지만 구현에 의존 최적화 등이다 구현에 의존 최적화로 (구현은 여전히 유효 뭔가 변경 한 경우 즉, 그들은 여전히 제대로 작동 것) Task.Delay(0)이었다을 허용 된 답변보다 낫습니다.


1
나는 여전히 4.5를 사용하고 있으며 약간의 연구를 할 때 Task.Delay (0)이 정적 CompletedTask 멤버를 반환하는 특별한 경우가 있다는 것을 알게되었습니다. 그런 다음 자신의 정적 CompletedTask 멤버에 캐시했습니다. : P
Darren Clark

2
Task.CompletedTaskVS2017에서 .net 버전을 4.6 (프로파일 7)으로 설정 한 경우에도 왜 그런지 모르겠지만 PCL 프로젝트에서 사용할 수 없습니다.
Felix

@Fay 나는 PCL API 표면의 일부가되어서는 안된다고 생각하지만 PCL을 지원하는 유일한 기능은 4.5도 지원하므로 이미 자체 Task.CompletedTask => Task.Delay(0);지원 을 사용 하여 지원해야하므로 내 머리 꼭대기에서 확실히 모르겠다.
Jon Hanna

17

최근 에이 문제가 발생하여 메소드가 무효라는 경고 / 오류가 계속 발생했습니다.

우리는 컴파일러를 배치하는 사업을하고 있으며 이것을 정리합니다.

    public async Task MyVoidAsyncMethod()
    {
        await Task.CompletedTask;
    }

이것은 지금까지 모든 조언 중 최고의 것을 모은 것입니다. 실제로 메소드에서 무언가를 수행하지 않는 한 return 문은 필요하지 않습니다.


17
그것은 완전히 잘못되었습니다. 메소드 정의에 비동기가 포함되어 있으므로 컴파일러 오류가 발생하므로 컴파일러가 기다립니다. "올바른"사용법은 public Task MyVoidAsyncMethog () {return Task.CompletedTask;}
Keith

3
이것이 가장 깨끗한 답변으로 보이는 이유는 확실하지 않습니다
webwake

3
Keith의 의견 때문에.
noelicus

4
그는 완전히 틀리지 않았습니다. 그는 단지 비동기 키워드를 제거했습니다. 내 접근 방식은 더 관용적입니다. 그는 최소한입니다. 조금 무례하지 않다면.
Alexander Trauzzi 2016 년

1
이것은 이해가되지 않습니다. 여기 Keith와 완전히 동의합니다. 실제로 모든 공감대를 얻지는 못합니다. 왜 필요하지 않은 코드를 추가 하시겠습니까? public Task MyVoidAsyncMethod() {}위의 방법과 완전히 동일합니다. 이와 같이 사용하는 유스 케이스가 있으면 추가 코드를 추가하십시오.
Nick N.

12
return Task.CompletedTask; // this will make the compiler happy


3

Task completedTask = Task.CompletedTask;.Net 4.6 의 솔루션을 선호 하지만 다른 방법은 메소드를 비동기로 표시하고 void를 반환하는 것입니다.

    public async Task WillBeLongRunningAsyncInTheMajorityOfImplementations()
    {
    }

경고가 표시되지만 (CS1998-대기 표현식이없는 비동기 함수)이 컨텍스트에서는 무시해도 안전합니다.


1
메소드가 void를 리턴하면 예외와 관련된 문제가있을 수 있습니다.
Adam Tuliper-MSFT

0

제네릭을 사용하는 경우 모든 답변에 컴파일 오류가 발생합니다. 사용할 수 있습니다 return default(T);. 자세한 내용은 아래 샘플을 참조하십시오.

public async Task<T> GetItemAsync<T>(string id)
            {
                try
                {
                    var response = await this._container.ReadItemAsync<T>(id, new PartitionKey(id));
                    return response.Resource;
                }
                catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
                {

                    return default(T);
                }

            }

왜 공감해야합니까?
Karthikeyan VK

문제는 비동기 메소드에 관한 것이 아닙니다 :)
Frode Nilsen

0
return await Task.FromResult(new MyClass());

3
이 코드가 문제를 해결하는 방법과 이유에 대한 설명포함 하여 질문을 해결할 수는 있지만 게시물의 품질을 향상시키는 데 도움이되고 더 많은 투표를 할 수 있습니다. 지금 질문하는 사람 만이 아니라 앞으로 독자들에게 질문에 대답하고 있음을 기억하십시오. 제발 편집 설명을 추가하고 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
데이비드 벅
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.