반응형

최근에 네트워크 서버를 개발하면서, 이에 따른 멀티스레드 구현을 하면서 몇가지 정리~

1 대의 서버가 N 개의 클라이언트들을 상대로 서비스를 하다보니 결국 멀티스레드로 구현하게 되었다. 네트워크 프로그래밍을 함에 있어서 스레드 사용은 필수라는 생각이 든다.

멀티 스레드에서 조심할 것은 하나의 자원에 대해서 여러개의 스레드가 동시에 접근하지 못하도록 제한을 걸어야 하는 것이다. 하나의 자원에 대해 두개 이상의 스레드가 동시에 접근할때 바로 OS 시간에 배웠던 Race Condition 상태가 발생할 수 있는 것인데, 보통 이를 해결하기 위해서 Mutex, Critical Section ( Critical Section 을 우리나라 말로는 "임계 영역" 이라고 하고, 일본에서는 "위험역(危險域) 이라고 한다는군), Semaphore 등을 사용 한다. 

Mutex 와 Critical Section 의 차이는  서로 다른 프로세스 간의 동기화도 지원하는 Mutex 와 달리 Critical Section 은 하나의 프로세스 내에서만 사용할 수 있다는 점이다. 그래서 보통 Mutex 는 프로그램의 중복 실행을 방지하는 용도로 많이 쓴다. Semaphore 는 뮤텍스와 비슷한데, 접근 가능한 스레드의 갯수를 지정할 수 있다는 점이 다르다.

외부에서 사용되는 변수를 스레드 내에서 변경하는 행위 등도 상당히 조심해야 한다. 특히 이 변수들이 스레드 외부 다른 곳에서 수정이 발생하는 경우 값이 꼬일 가능성이 커지므로 주의해야 한다.  이런 실수를 종종 하게 되는 이유는 스레드가 생성되는 순간부터 메인 프로세스 외에 스레드 함수가 병렬적으로 실행되고 있다는 것을 늘 머리속에 염두를 해 두어야 하는데, 사실 인간의 직관이 병렬 처리라는 개념으로 코딩하는게 쉽지 않기 때문이다.

서버와 클라이언트가 연결을 맺고 파일 전송과 같은 sequential 한 작업을 진행하는 도중에는 이 작업의 atomicity( DB 에서 트랜젝션 처리등에서 말하는 "원자성" 이다 ) 를 보존해 주는 처리가 있어야 한다. 예를 들면 파일 전송이 끝나지 않은 상태에서 또다른 파일을 열어서 같은 소켓으로 전송을 시도하려고 하는 경우등에 에러가 발생할 소지가 있을 수 있다.
 
여러 스레드가 동시에 파일을 읽는 경우, 동시에 파일을 쓰는 경우 등에 대해서도 잘 고려해 봐야 한다.
여기에 대해서는 친구가 좋은 팁을 알려줬는데, 일단 메모리상에 파일 버퍼에서 파일들을 저장해 놓고 있다가 큐에 일정 용량이 쌓이거나 일정 주기가 되면 그때마다 file write 를 하라는 것이다. client 가 주는대로 서버에서 그때마다 스레드 생성해서 각각이 file open 해서 파일을 쓰는 경우 상당히 퍼포먼스 측면에서 부담이 커진다. 그런데 현재 테스트 해 본 바로는 파일 크기가 그리 크지 않은 경우 ( 수백 kb 정도 ) 에서는 동시에 여러개 ( n < 10 ) 의 스레드로 파일 쓰기를 반복 시도 하여도 체감상 느껴지는 부하는 없었다.

지금은 최대한 심플하게 만들기 위해서 간단하게 비동기 소켓으로 만들고 있지만 보통 이렇게 다중으로 file write 를 해야 하는 경우 IOCP 를 많이 쓰는 것 같다. 이쪽은 나중에 시간나면 더 찾아봐야 겠다.

어쨌든 네트워크 프로그래밍은 어렵다. 일단 개발 및 테스트를 위해 장비가 최소 2 ~ n 개가 준비되어야 하고, 네트워크가 일시적으로 단절되는 경우 등 통신 상에서 필요한 여러가지 예외처리를 해 줘야 하는 부분들이 있다. 역시 많은 노하우가 필요하다고 생각된다.


+ Recent posts