인터페이스 구현을 비동기로 만들기


116

현재 일부 비동기 메서드를 사용하여 응용 프로그램을 만들려고합니다. 내 모든 IO는 인터페이스의 명시 적 구현을 ​​통해 수행되며 작업을 비 동기화하는 방법에 대해 약간 혼란 스럽습니다.

내가 보는 것처럼 구현에는 두 가지 옵션이 있습니다.

interface IIO
{
    void DoOperation();
}

OPTION1 : 암시 적 구현 비동기를 수행하고 암시 적 구현의 결과를 기다립니다.

class IOImplementation : IIO
{

     async void DoOperation()
    {
        await Task.Factory.StartNew(() =>
            {
                //WRITING A FILE OR SOME SUCH THINGAMAGIG
            });
    }

    #region IIO Members

    void IIO.DoOperation()
    {
        DoOperation();
    }

    #endregion
}

OPTION2 : 명시 적 구현 비동기를 수행하고 암시 적 구현에서 작업을 기다립니다.

class IOAsyncImplementation : IIO
{
    private Task DoOperationAsync()
    {
        return new Task(() =>
            {
                //DO ALL THE HEAVY LIFTING!!!
            });
    }

    #region IIOAsync Members

    async void IIO.DoOperation()
    {
        await DoOperationAsync();
    }

    #endregion
}

이러한 구현 중 하나가 다른 것보다 낫거나 내가 생각하지 않는 다른 방법이 있습니까?

답변:


231

이러한 옵션 중 어느 것도 올바르지 않습니다. 비동기식으로 동기 인터페이스를 구현하려고합니다. 그러지 마. 문제는 DoOperation()돌아올 때 작업이 아직 완료되지 않는다는 것입니다. 더 나쁜 것은 작업 중에 예외가 발생하면 (IO 작업에서 매우 일반적 임) 사용자는 해당 예외를 처리 할 기회가 없습니다.

해야 할 일은 인터페이스수정하여 비동기식으로 만드는 것입니다.

interface IIO
{
    Task DoOperationAsync(); // note: no async here
}

class IOImplementation : IIO
{
    public async Task DoOperationAsync()
    {
        // perform the operation here
    }
}

이렇게하면 사용자는 작업이 진행 async되고 있음을 알 수 있으며 작업을 수행 할 수 await있습니다. 이것은 또한 코드 사용자가로 전환하도록 강요 async하지만 피할 수 없습니다.

또한 StartNew()구현에서 사용 하는 것이 예일 뿐이라고 가정 하므로 비동기 IO를 구현하는 데 필요하지 않습니다. ( new Task()더 나쁜 것은 작동하지 않을 Start()Task입니다..)


명시 적으로 구현하면 어떻게 보일까요? 또한이 구현을 어디에서 기다리고 있습니까?
Moriya

1
@Animal Explicit 구현은 항상 똑같이 보일 것입니다 (단지 추가하십시오 async) : async Task IIO.DoOperationAsync(). 그리고 당신은 어디로 await돌아왔습 Task니까? 전화 할 때마다 DoOperationAsync().
svick

기본적으로 "어디에서 기다려야합니까?"라는 질문을 받아 들일 수 있다고 생각합니다. 비동기 메서드 내에서 기다리지 않으면 컴파일 경고가 표시됩니다.
Moriya

1
이상적으로는 IO 코드를에서 래핑 할 필요가 없습니다. Task.Run()IO 코드는 자체적으로 비동기식이어야하며 await직접 수행해야합니다. 예 line = await streamReader.ReadLineAsync().
svick

4
그렇다면 코드를 비 동기화하는 데 별 의미가 없습니다. 동기 메서드에 대해 비동기 래퍼를 노출해야합니까?
svick

19

더 나은 솔루션은 비동기 작업을위한 또 다른 인터페이스를 도입하는 것입니다. 새 인터페이스는 원래 인터페이스에서 상속되어야합니다.

예:

interface IIO
{
    void DoOperation();
}

interface IIOAsync : IIO
{
    Task DoOperationAsync();
}


class ClsAsync : IIOAsync
{
    public void DoOperation()
    {
        DoOperationAsync().GetAwaiter().GetResult();
    }

    public async Task DoOperationAsync()
    {
        //just an async code demo
        await Task.Delay(1000);
    }
}


class Program
{
    static void Main(string[] args)
    {
        IIOAsync asAsync = new ClsAsync();
        IIO asSync = asAsync;

        Console.WriteLine(DateTime.Now.Second);

        asAsync.DoOperation();
        Console.WriteLine("After call to sync func using Async iface: {0}", 
            DateTime.Now.Second);

        asAsync.DoOperationAsync().GetAwaiter().GetResult();
        Console.WriteLine("After call to async func using Async iface: {0}", 
            DateTime.Now.Second);

        asSync.DoOperation();
        Console.WriteLine("After call to sync func using Sync iface: {0}", 
            DateTime.Now.Second);

        Console.ReadKey(true);
    }
}

PS 비동기 작업을 다시 설계하여 실제로 void를 반환해야하는 경우가 아니라면 void 대신 Task를 반환합니다.


5
GetAwaiter().GetResult()대신 Wait()? 이렇게 AggregateException하면 내부 예외를 가져 오기 위해의 압축을 풀 필요가 없습니다 .
Tagc

변형은 여러 (명시적일 수 있음) 인터페이스를 구현하는 클래스에 의존합니다 class Impl : IIO, IIOAsync.. 그러나 IIO 및 IIOAsync 자체는 '이전 계약'을 최신 코드로 가져 오는 것을 방지 할 수있는 다른 계약입니다. var c = new Impl(); IIOAsync asAsync = c; IIO asSync = c.
user2864740
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.