적으려고 하다보니 쓰레드 관련 내용이 하도 많아서 쓰레드부분만 분리하려고 새 글 팠음...

 

 

아래 내용은 'Windows via C/C++'을 기반으로 하고 있습니다.

 

* 틀린거 많으니 교차검증 꼭 하세요 *


[1] 스레드

  1. 스레드 이론
    1. 스레드란? : '작업 흐름', 코드를 수행하는 흐름 단위. 프로세스의 주소 공간내에 있는 코드를 수행하고 데이터를 이용함.
    2. 스레드에는 '스레드 커널 오브젝트' (운영체제가 관리하기 위한 커널 오브젝트) / '스레드 스택' (코드 수행시 필요한 데이터를 담아두는 스택)이 포함
    3. 하나의 프로세스 내에 둘 이상의 스레드가 존재 할 수 있음. 이때 스레드들은 단일 주소 공간을 공유하며 동일한 코드를 수행할 수도 있으며 동일 데이터를 조작할수도 있음.
    4. 커널 오브젝트 핸들 테이블은 스레드별이 아닌 프로세스별로 존재함으로 동일 프로세스 내의 스레드들은 커널 오브젝트 핸들도 공유하게 됨
    5. 프로세스는 스레드보다 더 많은 리소스를 사용하기 때문에 프로세스를 생성하는 대신 추가적인 스레드를 생성하여 사용하는편이 좋음. 하지만 때때로는 추가적인 프로세스를 생성하는것이 더 좋을 수 있음.
      (ex : 구글 크롬의 경우 멀티프로세스를 생성하여 작동시키는 방법을 사용함)
  2. 스레드 이론 2
    1. 스레드 생성이 필요한 경우 : 스레드 별 우선순위를 설정하여 일부 작업을 백그라운드로 밀어 작업을 수행하게 하거나 입력 / 출력등의 중요한 업무를 분리시켜 작업할 수 있음.
    2. 스레드 생성을 하지 않아야 하는 경우 : 다중 스레드로 인해 동일 파일에 입출력이 동시에 적용되는경우 (프린트 되는 문서에 직접 수정가하는 등), 사용자 인터페이스 같은 단일화가 필요한 작업은 멀티 스레드를 사용하지 않는것이 좋음
    3. 멀티스레드보다 멀티 프로세스를 사용하는 경우 : 애플리케이션 내부의 실행단위를 독립적으로 수행하는 경우. (메모리를 공유하지 않고 독립적으로 사용하는 경우) 구글 크롬의 경우 각 탭별로 다른 프로세스를 할당하여 타 탭이 꺼지더라도 앱 전체가 꺼지지 않음.

[2] 스레드 스케쥴링

  1. 스케쥴링 기본 :
    1. 스레드는 정지(Suspended), 대기(Wait), 실행 (Run) 상태를 가짐. 정지된 스레드는 스케쥴링될 수 없음.
    2. 'Suspended thread' 명령 수행시, 커널모드에서 수행중인 코드는 비동기적으로 수행을 완료하나, 유저모드에서 수행중인 코드는 즉시 정지됨. (메모리 잠금이 일어날 수 있음)
      따라서, 해당 명령은 정지시키고자 하는 스레드가 어떤 작업을 수행중인지, 정지시 생길 수 있는 문제점과 해결책을 명확하게 파악한 후 사용해야함.
    3. 특정 프로세스 내 스레드를 전부 순회하며 정지를 시키면 프로세스를 정지시킨것과 유사한 효과를 낼 수는 있음. 하지만 자주 쓰이지는 않음.
    4. 스레드는 sleep() 함수를 호출하여 일정시간동안 스케쥴링 대상이 되지 않도록 명령을 내릴 수 있음. sleep 호출시 스레드는 자신에게 남은 Time slice (할당시간)을 포기함.
      함수의 인자로 ms 단위의 시간을 제공할 수 있으며, 0을 입력할 수 있음.
  2. 스레드 우선순위 :
    1.  

[3] 스레드 동기화

  1. 유저모드 동기화 :
    1. 동기화란? : 다수의 스레드가 공유 리소스에 접근해야 하며, 리소스가 손상되지 않아야 하는 경우, 특정 스레드가 타 스레드에게 작업이 완료되었음을 알려야 하는 경우에 이루어지는 행동. 이 경우 각 스레드들은 상호 통신을 수행함. 
      동기화가 수행되지 않으면 데이터 오염 및 경쟁-교착상태(Race Condition)에 빠질 수 있음
    2. '원자적 접근' (Atomic) : Atomic은 일반적으로 'Instruction 단위' (명령어 수행 단위)를 의미함. 특정 리소스에 Instruction 단위로 수행중일 때, 타 스레드는 동일 시간에 동일 리소스로 접근해서는 안됨.
    3. 인터락 : 명령 수행중 인터럽트 되지 않고 값을 원자적으로 조작할 수 있음. 인터락 함수를 사용 시 값의 변화를 보장 할 수 있으나, 인터락 함수의 대상인 리소스는 타 함수 (인터락이 아닌 함수)가 호출해서는 안됨.
      인터락 함수는 유저모드/커널모드 전환을 일으키지 않으며 속도가 매우 빠름.
      인터락 함수로 전달되는 값은 반드시 메모리 상에 정렬되어 있어야 함. (아니면 호출 실패)
    4. 스핀 락 : 공유 리소스 사용시 리소스 사용여부를 지속적으로 검사하는 방식. (Busy Wait)
      스핀락은 CPU 시간을 많이 낭비할 수 있으며, 스핀락을 사용하는 모든 스레드가 동일한 우선순위에 놓여 있어야만 함.
      또한, 스핀락 변수와 락을 걸고자 하는 데이터는 서로 다른 캐시 라인에 있는것이 좋음. 동일 캐시 라인이라면 리소스 사용중인 CPU는 동일 리소스에 접근하고자 하는 타 CPU와 경쟁하게 될 것.
      단일 코어 프로세서에서는 스핀락을 사용하지 않는것이 좋음. (CPU 시간 낭비)
    5. 크리티컬 섹션 : 공유 리소스에 대해 배타적으로 접근해야 하는 코드 집합. 공유 리소스를 다루는 여러줄의 코드를 "원자적으로 수행"하기 위한 방법.
      여기서 원자적이란, 위에 적힌것과 같이 현재 스레드가 리소스에 접근중일때는 다른 스레드가 동일 리소스에 접근 할 수 없음을 의미.
      크리티컬 섹션 사용시에는 Enter -> Leave 순으로 함수를 호출해야하며, Enter시 타 스레드가 크리티컬 섹션의 데이터를 사용 시 진입 불가, 미 사용시에는 진입이 가능하여 사용 할 수 있다.
      크리티컬 섹션 사용시, 데이터를 사용완료하고 나면 반드시 Leave를 호출해주어야 한다. (그렇지 않으면 나머지 모든 스레드가 해당 자원을 사용하지 못하게 됨)
    6. 크리티컬 섹션 2 : 크리티컬 섹션에 진입한 스레드가 매우 빠르게 자원을 리턴하여, 이후 스케쥴링 타임까지 시간이 낭비되는 등의 경우를 방지하기 위해 크리티컬 섹션 사용시 스핀락 메커니즘을 투입하여, 리소스 획득을 반복적으로 검사하게 할 수 있음. 
      크리티컬 섹션에 스핀락 메커니즘을 사용시, 일정 시간동안 스핀락을 수행하며 공유 리소스의 자원획득을 시도하고 실패시 스레드를 대기시키기 위해 커널모드로의 전환을 시도 함.
    7. SRWLock (Slim Reader-Writer Lock) : 크리티컬 섹션과 유사하나 값을 읽는 Reader와 값을 쓰는 Writer가 분리되어 있음. 공유 리소스에 동시에 Read를 실시하는것은 공유 리소스 값을 손상시키지 않기 때문에 동시에 다수의 수행이 일어나더라도 상관없음. 단, Writer가 리소스를 수정하는 동안에는 어떠한 Reader/Writer도 추가적으로 접근 해서는 안됨
    8. 조건변수
  2. 커널모드 동기화 :
    1. 개요 : 정확히는 커널 오브젝트를 이용한 스레드 동기화. 스레드를 동기화 하기 위해 커널 오브젝트 등을 어떻게 사용할것인가 에 대한 설명을 적을 예정.
    2. 시그널 / 논 시그널 : 시그널 상태 - 사용 가능 상태, 논 시그널 - 대기 상태 (사용중인 상태)
    3. 대기함수 : 대기함수 호출시 인자로 전달된 커널 오브젝트가 시그널 상태가 될때까지 '대기함수를 호출한 스레드'를 대기상태로 유지시킴.
      대기함수 호출시 커널 오브젝트가 시그널 상태면 대기상태로 전환되지 않음.
    4. 대기 함수 사용시 'dwMilliseconds'와 같은 대기시간 인자에 'INFINITE'를 전달 할 수 있음. 이때에는 스레드가 영원히 블로킹 되지 않도록 주의가 필요.
    5. '성공적인 호출', '성공적인 대기의 부가적인 영향' : 성공적인 호출을 통해 오브젝트의 상태가 변경 시 '성공적인 대기의 부가적인 영향' 이라고 칭함. (책에선 그러더라구요)
      '성공적인 호출'은 매개변수로 전달된 커널 오브젝트가 정상적으로 시그널 상태가 된 것을 의미.
  3. 커널 오브젝트 리스트 :
    1. 이벤트 커널 오브젝트 (이벤트 기반 동기화) : 이벤트 = '작업이 완료되었음을 알림'. 수동 리셋 이벤트 / 자동 리셋 이벤트가 존재.
      수동 리셋 이벤트 : 시그널 상태 시 대기중인 모든 스레드는 동시에 스케줄 가능한 상태가 됨
      자동 리셋 이벤트 : 시그널 상태 시 대기중인 오브젝트 중 하나만이 스케쥴 가능한 상태가 됨
    2. 대기 타이머 커널 오브젝트 (Waitable Timer) : 특정 시간 / 일정 시간 간격을 두고 시그널 상태가 되는 커널 오브젝트.
      특정 시간 / 간격에 맞춰서 작업을 수행해야하는 경우 사용.
      대기 타이머는 항상 논 시그널 상태로 생성됨.
      유저 타이머 (SetTimer를 사용)와의 차이점 - 대기 타이머는 커널 오브젝트임으로 다수의 스레드에서 공유될 수 있고 좀 더 보안에 안정적임.
    3. 세마포어 커널 오브젝트 : 리소스의 개수를 고려해야 하는 상황에서 주로 사용됨.
      최대 리소스 카운트, 현재 리소스 카운트를 저장하고 사용함.
      대기함수가 세마포어를 전달받을 시 현재 리소스 카운트 값을 확인하고 0보다 크면 값을 1 감소시키고 대기함수를 호출한 스레드를 스케줄 가능상태로 만듬. 
      현재 리소스 카운트가 0이면 (=세마포어가 논 시그널이면) 호출한 스레드를 대기상태로 유지시킴.

      작동 규칙 : 
      1. 현재 리소스 카운트가 0보다 크면 시그널
      2. 현재 리소스 카운트가 0이면 논시그널
      3. 현재 리소스 카운트는 0 미만이 될 수 없음
      4. 현재 리소스 카운트는 최대 리소스 카운트보다 커질 수 없음
    4. 뮤텍스 커널 오브젝트 (Mutual Exclusion ; 상호배제) : 크리티컬 섹션과 유사.

      작동 규칙 :
      1. 스레드 ID가 0 (유효하지 않은 스레드 ID)일 시 : 시그널 상태 ; 어떠한 스레드도 뮤텍스를 소지하지 않음
      2. 스레드 ID가 0이 아닌 경우 : 논 시그널 상태 ; 특정 스레드에 의해 소지중 
      3. (그 외 특수한 경우 규칙 위반 가능)
        1. 논시그널 상태에서도 스레드가 뮤텍스를 다시 소지가능
        2. 뮤텍스를 소지한 스레드가 소유권을 해지하지 않고 종료되는 경우, '버림' 처리 되어 소유권을 강제로 해제시킴

+ Recent posts