목록전체 글 (244)
방프리
Item 32 : 비동기 작업은 태스크 객체를 사용해 구성하라 태스크란 다른 리소스에 작업을 위임할 수 있도록 추상화한 개념이다. 태스크는 자기의 작업이 완료될 때 Task 객체를 반환하는데 이를 반복문에 사용하거나 여러 태스크를 처리하는 로직을 사용했을 때 대기를 해야하는 경우가 있다. 특히 Task.WhenAny()는 호출할 때마다 새로운 태스크를 생성하기에 TaskCompletionSource를 통해 관리를 할 수 있다. public static Task[] OrderByCompletion( this IEnuemerable tasks) { var sourceTask = tasks.ToList(); var completionSources = new TaskCompletionSource[sourceTa..
Item 31 : 불필요한 콘텍스트 마셜링을 피하라 어떤 콘텍스트에서도 실행될 수 있는 코드를 '자유 코드' 라고 한다. 기본적으로 개발자가 작성하는 코드는 콘텍스트 자유 코드이다. 반대로 특정 SynchronizationContext에서만 실행될 수 있는 코드를 '콘텍스트 인식코드'라고 한다. 콘텍스트 인식코드는 주로 GUI, HTTPContext 등에 사용되며 이 콘텍스트들은 다른 콘텍스트와 스위칭 되며 실행되도록 해야한다. 콘텍스트를 전환해서 문제를 피하는 것이 콘텍스트를 전환하지 않아 발생하는 문제를 해결하는 것보다 더 나은 선택이기 때문이다. 이를 위해 ConfigureAwait() 메서드를 사용한다. public static async Task ReadPacket(string url) { va..
Item 30 : 비동기 메서드를 사용해서 스레드 생성과 콘텍스트 전환을 피하라 비동기 메서드는 만능이 아니다. 로직의 수행 시간이 길면 그만큼 결과 도출이 늦어지기 때문에 비동기 메서드를 사용했다고 해서 복잡한 로직이 빠르게 수행된다는 생각을 가지면 안된다. 또한 잘 못된 생각으로 비동기 메서드에서 스레드를 생성하거나 다른 스레드로 콘텍스트를 전환한다고 해서 성능이 더 좋아지는 것도 아니다. 상황 별로 나누어보자 1. 파일 입출력은 비동기이지만 스레드가 아닌 I/O 완료 포트를 사용 2. 웹 요청도 비동기이지만 스레드가 아닌 네트워크 인터럽트 사용 3. GUI 어플리케이션은 UI 스레드가 메인 스레드이므로 다른 스레드에게 CPU 작업을 요청할 수 있음 (단, 실제로 적용하게 된다면 CPU 작업이 완료되..
Item 29 : 동기, 비동기 메서드를 함께 사용해서는 안 된다. 비동기 메서드와 동기 메서드를 사용할 때 두 가지 규칙을 지켜야 한다. 1. 비동기 작업이 완료될 때까지 기다리는 동기 메서드를 만들지 말라. 2. 수행 시간이 오래 걸리는 CPU 중식 작업을 비동기로 수행하지 말라. 위 규칙을 지키지 않으면 교착상태 (DeadLock)에 빠지기 쉽다. public static async Task ComputeUsageAsync() { try { var operand = await GetLeftOperandForIndex(19); var operand2 = await GetRightOperandForIndex(23); return operand + operand2; } catch (KeyNotFoundE..
Item 28 : async void 메서드는 절대 작성하지 말라 해당 챕터는 C#의 동시성 처리 관련 책을 읽었을 때 그 저자도 강조하던 멘트였다. (Link : C# 동시성 프로그래밍 (Concurrency in C# Cookbook) https://book.interpark.com/product/BookDisplay.do?_method=detail&sc.saNo=001&sc.prdNo=349595515&product2020=true) 그 이유는 async void는 대기할 수 없다. 즉, 앞 챕터에서 이야기 했던 async await의 장점을 살릴 수가 없다. 그럼에도 불가피하게 사용해야하는 경우가 있는데 바로 비동기 이벤트 핸들러를 만들 때이다. async void를 사용하지 않아야 하는 이유 중..
Item 27 : 비동기 작업에는 비동기 메서드를 사용하라 처음 async await을 이해할 때 굉장히 복잡했다. 어떤 매커니즘으로 돌아가는지??... C++을 배울 땐 이런 개념이 없었고 CriticalSection, Mutex 같은 락 처리와 비슷한 내용이라 생각했다. 하지만 실제 업무에서 조금씩 사용해보면서 async await의 편리함과 내부 기능에 대해 이해가 될수록 굳이 복잡하게 이해할 필요가 없음을 느꼈다. 일단 async await을 사용하면 컴파일러가 비동기 메서드를 처리할 때는 비동기 작업을 시작하기 위한 기반 구조를 생성한다. 비동기 작업이 완료되면 이후의 작업을 계속 진행하게 하는데 대부분의 await 구문을 컴파일 하는 과정에서 이루어진다. 컴파일러는 await 구문 이후의 코드..
Item 26 : 지역 함수를 사용해서 반복자와 비동기 메서드의 오류를 즉시 보고하라 메서드에서 어떤 내용을 수행하다가 발생되는 예외는 즉시 발생되어 처리되어야 한다. 하지만 반복자 메서드와 비동기 메서드는 예외가 생겼을 때 바로 출력되지 않고 정상적으로 처리되는 듯 넘기고 실제 사용하는 부분에서 예외를 발생시킨다. 이는 반복자 메서드와 비동기 메서드가 코드를 재구성하기 때문이다. 즉, 예외가 발생되더라도 어느 시점에서 발생된 예외인지 추적이 어렵다. //반복자 메서드에서 예외가 발생하는 순간 public IEnumerable GenerateSample(IEnumerable sequence, int sampleFrequency) { if (sequence == null) { throw new Argume..
Item 25 : 배열 매개변수에는 params 배열만 사용해야 한다 배열은 반공변을 지원하지 않는다. 그렇기 때문에 배열형태의 매개변수를 받을 수 있는 것에 제한이 크다. 그럴 땐 IEnumerable 형태로 사용하는 것이 좋다. class B { public static B Factory() => new B(); public virtual void WriteType() => WriteLine("B"); } class D1 : B { public static new B Factory() => new D1(); public override void WriteType() => WriteLine("D1"); } class D2 : B { public static new B Factory() => new D2..