Microsoft MVP성태의 닷넷 이야기
글쓴 사람
홈페이지
첨부 파일
 

C# - 동기 방식이면서 비동기 메서드처럼 구현한 사례

일반 메서드를 필요하다면 Async로 만드는 방법은 Task 객체를 반환하도록 하면 됩니다. 가령, 5초 동안 스레드를 중지하는 다음의 메서드에 대해,

using System.Threading;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            new MyClass().Test();
        }
    }

    class MyClass
    {
        public void Test()
        {
            Thread.Sleep(1000);
        }
    }
}

TestAsync 메서드를 다음과 같이 만들어 줄 수 있습니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await new MyClass().TestAsync();
            Console.WriteLine("End");
        }
    }

    class MyClass
    {
        public Task TestAsync()
        {
            Task t = new Task(() => Thread.Sleep(1000));
            t.Start();
            return t;
        }
    }
}

그런데, Task 객체를 만들지 않으면서 Async 기능을 구현한 것처럼 만들 수도 있습니다. 이럴 때 TaskCompletionSource 객체를 사용하면 됩니다.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await new MyClass().TestAsync2();
            Console.WriteLine("End: TestAsync2");
        }
    }

    class MyClass
    {
        public Task TestAsync2()
        {
            TaskCompletionSource<object> src = new TaskCompletionSource<object>();

            try
            {
                Console.WriteLine("TestAsync2 called!");
                src.SetResult(null);
            }
            catch (Exception e)
            {
                src.SetException(e);
            }

            return src.Task;
        }
    }
}

뭐 저런 쓸모 없는 코드가 다 있나... 할 수 있습니다. 그런데 이런 쓸모없는 코드를 .NET BCL에서 찾아 볼 수 있습니다.

// System.Data.dll
// System.Data.Common.DbConnection

public virtual Task OpenAsync(CancellationToken cancellationToken)
{
    TaskCompletionSource<object> source = new TaskCompletionSource<object>();
    if (cancellationToken.IsCancellationRequested)
    {
        source.SetCanceled();
    }
    else
    {
        try
        {
            this.Open();
            source.SetResult(null);
        }
        catch (Exception exception)
        {
            source.SetException(exception);
        }
    }
    return source.Task;
}

위에서 보는 바와 같이 System.Data.Common.DbConnection은 OpenAsync 메서드를 제공하지만, 이름과는 달리 내부적으로 처리는 완전하게 동기로 하고 있습니다.

이런 바보 같은 코드가 왜 나왔을까요? 사실 DbConnection은 추상 클래스이기 때문에 기본적인 기능만 제공하는 정도입니다. 그리고 실질적인 기능들은 이를 상속받은 타입들이 하게 되는데, 자식 클래스들에게 비동기를 강제로(?) 구현하게 짐을 지우는 것이 어떤 경우에는 부담이 될 수 있습니다. 그래서, 인터페이스 측면에서 비동기 기능을 제공하는 가상 메서드는 제공하지만 동기처럼 동작하는 저런 구현이 나온 것입니다.

실제로 System.Data.SqlClient의 SqlConnection은 OpenAsync를 다시 구현하면서 비동기 기능을 완전하게 제공합니다.

그 외에도, 일정에 쫓겨 비동기 기능을 구현하진 못했지만 언젠가(?) 꼭 구현하리라 다짐하고 Async 메서드를 만들어 둔다면 저런 식으로 할 수 있을 것입니다. ^^




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]





[최초 등록일: ]
[최종 수정일: 1/11/2018 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 




... 16  17  18  19  20  21  22  23  24  [25]  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
11521정성태5/15/20181770개발 환경 구성: 377. Azure - 원하는 성능 데이터로 모니터링 대시보드 구성
11520정성태5/12/20182328.NET Framework: 748. C# 7.1 - 참조 어셈블리(Ref Assemblies)
11519정성태3/13/20192490개발 환경 구성: 376. ASP.NET Web Application 프로젝트의 FileSystem 배포(Publish) 시 Before/After Task 설정 방법 [1]
11518정성태5/11/20182179.NET Framework: 747. C# 7.0에서도 부분적으로 가능해진 "타입 추론을 통한 튜플의 변수명 자동 지정"
11517정성태5/10/20182417.NET Framework: 746. Azure runbook 예제 - 6시간 동안 수행 중인 VM을 중지 [1]파일 다운로드1
11516정성태5/9/20181886.NET Framework: 745. Azure runbook을 PowerShell 또는 C# 코드로 실행하는 방법파일 다운로드1
11515정성태6/8/20182182.NET Framework: 744. C# 6 - Expression bodied function
11514정성태5/3/20183613오류 유형: 466. Bitvise - Error in component session/transport/kexHandler [2]
11513정성태5/10/20184227.NET Framework: 743. C# 언어의 공변성과 반공변성 [5]파일 다운로드1
11512정성태5/3/20182005개발 환경 구성: 375. Azure runbook 실행 시 "Errors", "All Logs"에 오류 메시지가 출력되는 경우
11511정성태5/2/20182160개발 환경 구성: 374. Azure - Runbook 기능 소개
11510정성태4/30/20183814.NET Framework: 742. windbg로 확인하는 Finalizer를 가진 객체의 GC 과정파일 다운로드1
11509정성태4/28/20183352.NET Framework: 741. windbg로 확인하는 객체의 GC 여부
11508정성태4/23/20182536개발 환경 구성: 373. MSBuild를 이용해 프로젝트 배포 후 결과물을 zip 파일로 압축하는 방법파일 다운로드1
11507정성태9/29/20182632개발 환경 구성: 372. MSBuild - 빌드 전/후, 배포 전/후 실행하고 싶은 Task 정의
11506정성태6/27/20185101.NET Framework: 740. C#에서 enum을 boxing 없이 int로 변환하기 - 두 번째 이야기 [6]파일 다운로드1
11505정성태4/19/20182224개발 환경 구성: 371. Azure Web App 확장 예제 - Simple WebSite Extension
11504정성태4/19/20182369오류 유형: 465. Azure Web App 확장 - Extplorer File manager 적용 시 오류
11503정성태4/19/20182302오류 유형: 464. PowerShell - Start-Service 명령 오류 (Service 'xxx' cannot be started)
11502정성태5/12/20183170개발 환경 구성: 370. Azure VM/App Services(Web Apps)에 Let's Encrypt 무료 인증서 적용 방법 [2]
11501정성태4/17/20182202개발 환경 구성: 369. New-AzureRmADServicePrincipal로 생성한 계정의 clientSecret, key 값을 구하는 방법파일 다운로드1
11500정성태4/17/20182450개발 환경 구성: 368. PowerShell로 접근하는 Azure의 Access control 보안과 Azure Active Directory의 계정 관리 서비스
11499정성태4/17/20182042개발 환경 구성: 367. Azure - New-AzureRmADServicePrincipal / New-AzureRmRoleAssignment 명령어
11498정성태4/17/20182081개발 환경 구성: 366. Actvie Azure Directory의 사용자 유형 구분 - Guest/Member
11497정성태4/17/20182265개발 환경 구성: 365. Azure 리소스의 액세스 제어(Access control) 별로 사용자에게 권한을 할당하는 방법 [2]
11496정성태4/17/20181797개발 환경 구성: 364. Azure Portal에서 구독(Subscriptions) 메뉴가 보이지 않는 경우
... 16  17  18  19  20  21  22  23  24  [25]  26  27  28  29  30  ...