목록전체 글 (247)
방프리
Item 44 : 동적 타이핑의 장단점을 이해하라Cast는 제네릭 메서드로, T타입으로 타입 변환이 가능하기만 하다면 어떤 타입이든 사용할 수 있다. 하지만 Cast의 한계에 대해서 제대로 알지 못하면 예상대로 동작하지 않기에 제대로 알고 사용해야한다.// 동작되지 않는 코드var answer1 = GetSomeStrings().Cast();try{ foreach (var v in answer1) WriteLine(v);}catch (INvalidCastException){ WriteLine("Cast Failed!");}Cast는 인수 런타임 타입에서 제공하는 사용자 정의 변환 함수에는 접근할 수 없다. 단순히 참조 변환가 박싱 변환만 가능하기에 Cast는 T가 System.Ob..
Item 42 : 잠김 영역에서는 외부 코드 호출을 삼가라lock을 통해 보호하는 영역 내에서 리스너를 통해 함수를 호출하는 방식은 교착상태를 불러 일으킬 수 있다.public class WorkerClass{ public event EventHandler RaiseProgress; private object syncHandle = new object(); public void DoWork() { for (int count = 0; count 위와 같이 코드를 작성하였을 때 Progress 객체에 접근할 때와 engine_RaiseProgress 함수를 호출할 때 교착상태가 발생할 확률이 매우 크다. 이 문제는 아래와 같이 수정하여 회피하는 것이 좋다...
Item 41 : 락은 가능한 한 좁은 법위에 적용하라 private 접근제한자를 통해 멤버 변수를 관리할 범위를 최소화하는 것처럼 lock 또한 최소 단위로 관리해야 한다. 필자는 lock을 관리하는 총 3가지 방식을 제안한다 1. MethodImplAttribute를 사용해서 메서드 전체를 보호한다. 가장 드물게 사용 [MethodImpl(MethodImplOptions.Synchronized)] public void IncrementTotal() { total++; } 2. 개발자에게 현재 타입이나 현재 객체에만 락을 걸도록 의무화한다. 즉, 모두가 lock(this)나 lock(MyType)을 사용하도록 권장한다. 하지만 위의 방식은 전 세계 개발자 모두가 가이드를 지켜야 하므로 수행되기 어려움 ..
Item 40 : 동기화에는 lock()을 최우선으로 사용하라 동기화 요소는 특정 스레드가 임계영역 내에서 연산을 수행하는 동안 다른 스레드로부터 이를 보호하는 역할을 수행한다. .NET 기본 클래스 라이브러리에서는 다양한 기능을 통해 공유 데이터에 대한 접근을 안전하게 동기화하는데 특히 Monitor.Enter()와 Monitor.Exit()는 특별하게 다루어진다. 하지만 lock()이 가장 기본이 되는 기능이므로 최우선적으로 고려 시에는 lock()을 먼저 사용하는 것이 좋다. lock()은 값 타입을 지원하지 않는다. 그러기에 Monitor.Enter()을 사용하게 되는데 이 때 주의할 점이 있다. 하단의 코드는 정상적으로 컴파일은 되지만 문제가 발생할 수 있다. public void Increme..
Item 39 : XAML 환경에서 스레드 간 호출을 이해하라 이벤트 핸들러를 좀 더 쉽게 작성하고자 할 때 제네릭을 활용하여 다음과 같이 작성해볼 수 있다. //Background 스레드 사용 시 public static class XAMLControlExtensions { public static void InvokeIfNeeded( this System.Windows.threading.DispatcherObject ctl, Action doit, system.Windows.Threading.DispatcherPriority priority) { if (System.Threading.Thread.CurrentThread != ctl.Dispatcher.Thread) { ctl.Dispatcher.In..
Item 38 : 스레드 간 커뮤니케이션에는 BackgroundWorker를 사용하라 BackgroundWorker는 ThreadPool을 이용하도록 만들어졌고 스레드 간 커뮤니케이션을 위한 여러 기능을 제공한다. 기존 QueueUserWorkItem의 단점 1. WaitCallback에서 발생하는 예외처리를 처리해야하는데 이 메서드가 예외를 던지면 전체 어플리케이션이 종료된다. 2. 백그라운드 스레드와 포그라운드 스레드 간 커뮤니케이션을 위한 기능을 전혀 제공하지 않는다. 만약 필요하다면 QueueUserWorkItem()의 기능을 활용해 만든 BackgroundWorker 컴포넌트를 사용 BackgroundWorker 사용방법 BackgroundWorker backgroundWorkerExample ..
Item 37 : 스레드를 생성하지 말고 스레드 풀을 사용하라 스레드 개수를 개발자가 관리하기엔 무리가 있다. CLR에서 가비지 컬렉터 사용을 위해 스레드를 얼마나 할당해야되는지 알 수 없고 이외에도 Task를 관리하기 위해 얼마나 생성해야하는지 모르기 때문이다. 그러기에 우리는 스레드풀 세팅을 통해 스레드들이 스레드 풀에서 관리하도록 해야한다. 또한 스레드 풀이 태스크의 생명주기도 관리하기 때문에 스레드 풀을 적극적으로 사용해야 한다. 스레드 풀이 태스크의 작업 진행 상황을 리소스에 기초하여 할당하고 부하를 적당히 분산하기에 부하 분산 로직을 개발자가 만들 필요가 없다. 적절한 스레드의 개수를 찾아내는 건 코어 수에 비례해 찾는건 너무 고전적인 해법이라 필자는 이야기 한다. 그럼 어떤 방식으로 찾아야할..
Item 36 : 예외를 염두에 두고 병렬 알고리즘을 만들라 PLINQ는 자식 스레드가 잘못 동작하는 가능성에는 고려하지 않았는데 실제로는 언제든지 문제가 발생할 수 있다. 단 이 처리의 몫은 항상 개발자가 져야 한다. 예외는 스레드의 경계를 넘어 다른 스레드에 전달될 수 없다. 예외가 발생할 경우 예외를 백그라운드 작업을 수행하는 태스크를 떠나지 않게 해야된다. 예로 TaskCompletionSource 클래스를 사용할 경우에는 TrySetException()을 호출해서는 안되고 태스크의 완료 상황을 나타내기 위해 TrySetResult()를 어떻게든 호출해야 한다. private static Task startDownload(string url) { var tcs = new TaskCompletion..