윈도우즈 기반의 개발에 익숙해져 있는 개발자라면 IOCP의 강력함과 고마움을 리눅스 개발자 만큼 절실히 느끼는 개발자는 드물것이라 생각한다.
내가 이 글을 쓰는 이유는 리눅스에서 윈도우의 IOCP와 비슷한 기능을 구현해, 쓰레드의 효율성을 높이고
싶어하는 개발자를 위한 팁을 소개하기 위해서이다..
조건,
여기에서 가장 큰 관건은, 바로 작업을 수행해야 하는 쓰레드를 결정하는 것이다.
보편적으로 많이 소개되는 방법은 pthread_cond_wait() 함수를 사용하는 방법이 있다.
그럼, 이 부분을 어떻게 해결을 하면 좋을까? 물론, 여기에서 sleep(0)를 사용하라거나, printf() 를 사용하라는
해결방법을 제시하기 위해 글을 쓰는 것은 아니다.
해답은 쓰레드 간의 작업 전환이다!!
위의 문제해결 방법도 쓰레드의 작업을 강제로 전환하면서 해결된 부분인것이다.
이러한 함수들은 일명 System call이라고 불리우는 함수들로, 커널은 이러한 함수가 호출되어 질때 쓰레드의
작업 전환을 처리한다. (이 부분에 대한 상세한 내용은 각자 찾아 보시길~)
일부 개발자들은, 쓰레드간의 작업전환(컨텍스팅 스위칭)을 많이 하면 퍼포먼스가 떨어진다고 한다.
물론, 맞는 말이다!! 그러나, 반대로 작업전환이 없다면 여러개의 쓰레드가 처리를 수행할 CPU를 할당 받지 못해
좀비가 되는 상황도 발생이 된다. -- 심지어는 I/O 작업과 같은 처리를 하지 못해 어플리케이션에 문제가 생기는
경우도 발생을 하게 된다.
결론은 이러한, 쓰레드간의 작업 전환이 프로세싱을 유연하게 만들어 준다는 것이다.
- 데이터를 만들었다면, -> 데이터를 기록하고 -> 데이터를 읽고 -> ...
이러한 처리가 반복이 되어야 하는데 스위칭이 없다면 한곳에 묶여 버리는 문제가 생기기 때문이다.
그럼 어떠한 방식으로 해결을 하면 좋을 것인가? 바로 알아 보면..
커널에는 IPC가 있다. IPC중에 세마포어라는 Locking 알고리즘이 존재한다. 개발자라면 이 세마포어가 얼마나
느린지 충분히 알고 있으리라 생각한다. - 그러나, 다른 Locking 알고리즘에 비해 우수한 장점이 있다.
눈치 빠른 개발자는 답을 구했으리라 본다.
그럼 적용방법을 보자..
내가 이 글을 쓰는 이유는 리눅스에서 윈도우의 IOCP와 비슷한 기능을 구현해, 쓰레드의 효율성을 높이고
싶어하는 개발자를 위한 팁을 소개하기 위해서이다..
조건,
- N개의 쓰레드로 하여금 프로세싱 처리를 하게 하고 싶다.
- 그런데, 문제는 어떤 쓰레드에 작업을 줘야 할지 판단하기 어렵다~
- 그런데, 문제는 어떤 쓰레드에 작업을 줘야 할지 판단하기 어렵다~
여기에서 가장 큰 관건은, 바로 작업을 수행해야 하는 쓰레드를 결정하는 것이다.
보편적으로 많이 소개되는 방법은 pthread_cond_wait() 함수를 사용하는 방법이 있다.
이 방법으로 개발해본 개발자라면 이러한 경우를 당해 봤을 것이다..
쓰레드의 CPU 사용 점유율 높아지는 경우, 제어가 제대로 되지 않을 문제
이 상황에서 개발자 들이 많이 쓰는 방법이 sleep(0)와 같은 방법일 것이다.
아니면 printf()와 같은 함수이거나..
= 가끔 이런 상황에서 printf()를 통해 문자열을 출력하는 경우 발생되지 않는 경험도 해 봤을것이다.
그럼, 왜 이렇게 처리를 하면 문제가 해결되는 것일까?
= 메뉴얼을 많이 찾아본 개발자는 이미 답을 알지도 모르겠다!!
오늘의 키포인트가 바로 이 부분이다~
쓰레드의 CPU 사용 점유율 높아지는 경우, 제어가 제대로 되지 않을 문제
이 상황에서 개발자 들이 많이 쓰는 방법이 sleep(0)와 같은 방법일 것이다.
아니면 printf()와 같은 함수이거나..
= 가끔 이런 상황에서 printf()를 통해 문자열을 출력하는 경우 발생되지 않는 경험도 해 봤을것이다.
그럼, 왜 이렇게 처리를 하면 문제가 해결되는 것일까?
= 메뉴얼을 많이 찾아본 개발자는 이미 답을 알지도 모르겠다!!
오늘의 키포인트가 바로 이 부분이다~
그럼, 이 부분을 어떻게 해결을 하면 좋을까? 물론, 여기에서 sleep(0)를 사용하라거나, printf() 를 사용하라는
해결방법을 제시하기 위해 글을 쓰는 것은 아니다.
해답은 쓰레드 간의 작업 전환이다!!
위의 문제해결 방법도 쓰레드의 작업을 강제로 전환하면서 해결된 부분인것이다.
이러한 함수들은 일명 System call이라고 불리우는 함수들로, 커널은 이러한 함수가 호출되어 질때 쓰레드의
작업 전환을 처리한다. (이 부분에 대한 상세한 내용은 각자 찾아 보시길~)
일부 개발자들은, 쓰레드간의 작업전환(컨텍스팅 스위칭)을 많이 하면 퍼포먼스가 떨어진다고 한다.
물론, 맞는 말이다!! 그러나, 반대로 작업전환이 없다면 여러개의 쓰레드가 처리를 수행할 CPU를 할당 받지 못해
좀비가 되는 상황도 발생이 된다. -- 심지어는 I/O 작업과 같은 처리를 하지 못해 어플리케이션에 문제가 생기는
경우도 발생을 하게 된다.
결론은 이러한, 쓰레드간의 작업 전환이 프로세싱을 유연하게 만들어 준다는 것이다.
- 데이터를 만들었다면, -> 데이터를 기록하고 -> 데이터를 읽고 -> ...
이러한 처리가 반복이 되어야 하는데 스위칭이 없다면 한곳에 묶여 버리는 문제가 생기기 때문이다.
그럼 어떠한 방식으로 해결을 하면 좋을 것인가? 바로 알아 보면..
커널에는 IPC가 있다. IPC중에 세마포어라는 Locking 알고리즘이 존재한다. 개발자라면 이 세마포어가 얼마나
느린지 충분히 알고 있으리라 생각한다. - 그러나, 다른 Locking 알고리즘에 비해 우수한 장점이 있다.
첫번째는, sleep()이나 printf()와 같은 System call 함수라는 것이다.
두번째로, 기본적으로 카운팅락이 된다는 것이다.
두번째로, 기본적으로 카운팅락이 된다는 것이다.
눈치 빠른 개발자는 답을 구했으리라 본다.
그럼 적용방법을 보자..
초기화
1) 쓰레드로 공유할 세마포어를 생성한다.
쓰레드
2) 사용하고자 하는 쓰레드에서 해당 세마포어의 Lock 상태로 대기한다. (일단, wait상태가 된다)
4) 데이터가 발생되면, Lock이 설정되고 세마포어 대기에서 해제된다.
5) 데이터 큐에서 처리할 데이터를 처리한다.
6) 처리를 마친 리소스를 반환한다.
7) 다시 2)에서 부터 시작
메인
3) 쓰레드로 할당할 데이터가 있다면, 데이터 큐에 데이터를 저장하고, 세마포어를 Unlock 한다.
1) 쓰레드로 공유할 세마포어를 생성한다.
쓰레드
2) 사용하고자 하는 쓰레드에서 해당 세마포어의 Lock 상태로 대기한다. (일단, wait상태가 된다)
4) 데이터가 발생되면, Lock이 설정되고 세마포어 대기에서 해제된다.
5) 데이터 큐에서 처리할 데이터를 처리한다.
6) 처리를 마친 리소스를 반환한다.
7) 다시 2)에서 부터 시작
메인
3) 쓰레드로 할당할 데이터가 있다면, 데이터 큐에 데이터를 저장하고, 세마포어를 Unlock 한다.
이러한 순서로 처리하면, 쓰레드는 자연스럽게 작업전환이 발생되고 특정 쓰레드에 집중되어 처리되는 것을
막을수 있다.
-- 여기서 주의 할 것은 데이터큐에 접근하기 위한 lock처리를 해야 완전한 구조가 된다. 이때는 mutex가 적합.
(spinlock을 사용할 경우, 특정쓰레드의 작업전환에 문제가 발생될 경우가 있다.
특히, 싱글 CPU는 spinlock을 사용한 이득을 전혀 볼수 없다~ 멀티 CPU에서 사용가능한 lock방식이기 때문)
한번 시도해 보시길^^~
참고로, 위의 방식이 어떻게 처리되는지를 간단한 사용예제를 첨부한다.