언어/C#

CLR 단순 동기화1

tsyang 2022. 5. 1. 04:09

스레드 동기화

스레드 동기화는 일반적으로 다수의 스레드가 공유 데이터에 '동시에' 접근하는 경우에도 데이터가 손상되는 것을 막기 위해서 사용된다.

 

그러나 스레드 동기화는 많은 문제를 가지고 있다. 

 

우선 오류를 발생시킬 가능성이 높다. 왜냐? 코드를 작성할 때 여러 스레드에서 동시에 접근할 수 있는 데이터들을 모두 확인한 후, 스레드 동기화 락을 데이터 접근 부분에 모두 적용해야 한다. 만약 락을 한 군데라도 빠트리면 오류가 발생한다. 테스트도 매우 힘들다. 유일한 테스트는 그냥 여러번 실행시켜 본뒤 문제가 발생하기 않길 비는 수밖에... 그리고 이런 테스트는 여러 개의 CPU를 가진 컴퓨터에서 해보는게 좋다. 왜냐? 그래야 여러 개의 스레드가 동시에 리소스에 접근할 가능성이 높기 때문이다.

 

성능이 떨어지는 문제도 있다. 왜냐? 락을 획득하고 해제하는 과정은 추가적인 함수 호출을 수반하고, 어떤 스레드가 락을 획득하도록 할 것인지도 결정해야 한다. 어떤 종류의 락을 사용하냐에 따라 다르지만, 단순 연산의 경우 수배 이상 차이가 난다.

 

마지막으로 스레드가 많이 생성되는 문제이다. 왜냐? 락은 하나의 스레드만 리소스에 접근할 수 있게 하지만 이것이 더 많은 스레드가 생성되도록 만든다. 더 나쁜건 블로킹 되었던 스레드가 다시 수행되는 경우이다. 이러면 스케줄링이 복잡해지고 더 많은 컨텍스트 스위치가 발생한다.

 

따라서, 스레드 동기화는 가능한 사용하지 않도록 설계해야 한다. 즉, 정적 데이터같은 공유 데이터를 가능한 쓰지 말아야 한다는 뜻이다.

 

 

Thread-Safe한 메서드

마이크로 소프트의 FCL은 모든 정적 메서드가 스레드 안전(Thread-Safe)한 메서드임을 보장한다. 반면 인스턴스 메서드의 경우 그렇지 않다.

 

스레드 안전하게 메서드를 만든다는 것이 내부적으로 동기화 락을 사용해야 한다는 것을 의미하지 않는다. 예를 들어 다음의 메서드는 스레드 안전하다.

 

    public static int Max(int a, int b)
    {
        return a < b ? a : b;
    }

 

부적으로 공유 데이터에 접근하지 않기 때문이다

 

 스레드가 어떤 객체를 생성하면 이 객체에 대한 참조는 객체를 생성한 스레드만 알고 있다. 즉, 다른 스레드는 이 객체에 접근할 수 없기 때문에 인스턴스 메서드를 호출해도 스레드 동기화가 필요하지 않다. 하지만 스레드가 객체의 참조를 외부에 노출하면서(Task에 전달하거나 QueueUserWorkItem의 state로 활용) 이를 읽기 전용으로 사용하지 않는다면 동기화가 필요하다.

 

만약 클래스 라이브러리를 만든다면 FCL처럼 정적 메서드는 스레드-안정적으로 구성하고 인스턴스 메서드는 그렇지 않게 하는게 좋다.

 

 

단순 동기화 요소

단순 동기화 요소에는 유저 모드 동기화 요소커널 모드 동기화 요소가 존재한다.

 

가능하면 유저 모드 동기화 요소를 사용하는 것이 좋다. 왜냐? 유저 모드 동기화는 특수한 CPU 명령을 사용하기 때문에 커널 모드에 비해 더 훨씬 빠르게 수행된다. 또한 윈도우 운영체제가 스레드가 불로킹 되었는지 알지 못하며 따라서 새로 스레드를 생성하지도 않는다. 즉 아주 좋다.

 

그러나 이 말은 곧 윈도우 운영체제만이 유일하게 스레드를 중단 시킬 수 있다는 것이다. 따라서 스레드가 획득할 수 없는 리소스를 반복적으로 요구할 가능성이 있으며 CPU 시간을 낭비하게 될 수 있다.

 

따라서, 커널 모드 동기화 요소가 필요하다. 왜냐?  커널 모드 동기화 요소는 윈도우 운영체제가 제공하는 기능이다. 따라서 만약 이미 차지된 리소스를 스레드가 획득하려 하면 윈도우 운영체제가 해당 스레드를 블로킹 해버릴 수 있다.  그러나 유저모드와 커널 모드간의 스위칭은 상당한 성능 저하를 일으킨다. 

 

어떤 스레드가 동기화 요소를 소유한 채로 해제하지 않으면 이를 대기하던 스레드가 영원히 블로킹되어버릴 수 있는데, 만약 이런 요소가 유저 모드 요소라면 라이브락이라 부르고, 커널 모드라면 데드락이라 부른다. 둘 다 안 좋지만 그나마 데드락이 낫다. 왜냐? 데드락은 메모리만을 허비하는데 비해, 라이브락은 CPU도 허비하기 때문이다.


참고 : 제프리 리처, CLR via C# 4판 , 비제이퍼블릭, 29장. 단순 스레드 동기화 요소  [883~919p]

'언어 > C#' 카테고리의 다른 글

단순동기화3 - 커널 모드 동기화  (0) 2022.05.09
단순동기화2 - 유저 모드 동기화  (0) 2022.05.06
CLR 스레딩3 - 태스크(Task)  (0) 2022.04.24
CLR 스레딩2 단순 계산 작업  (1) 2022.04.17
CLR 스레딩1 기본  (0) 2022.04.10