본문 바로가기

이것 저것 정리

Lock 종류(1) - Spin Lock, Mutex, Critical Section, Semaphore

오늘은 Lock 종류에 대해 정리 해보았다.

(여기선 따로 Lock 필요성에 대해 정리하지 않겠음.)

(정리하다 보니 내가 읽은 글들을 해석, 정리(...? 나열 ㅋㅋㅋ...) 해둔 느낌밖에 없는데,

나를 위한 글이라 생각하고 뻔뻔해져 보겠다.)

 

  • Spin Lock (스핀 락)

https://sirum.tistory.com/entry/%EC%BB%A4%EB%84%90-%EB%A0%88%EB%B2%A8-%EC%8A%A4%EB%A0%88%EB%93%9C-vs-%EC%9C%A0%EC%A0%80-%EB%A0%88%EB%B2%A8-%EC%8A%A4%EB%A0%88%EB%93%9C

 

커널 레벨 스레드 vs 유저 레벨 스레드

이것 저것 내용들을 정리하려다가 아래의 글을 발견했는데 단계적으로 용어를 정의하고 커널 레벨 스레드란 결국 무엇이고, 유저 레벨 스레드가 뭔지 쉽게 접근하여 설명해준 글이 있어 정리

sirum.tistory.com

바로 전 글에서 다음과 같이 정리 한 적이 있다.

CPU 는 하나의 프로세스만 처리 가능.

그렇다면 커널 레벨 멀티프로세서에서는 동기화를 어떻게 맞출까?

 

그 중 하나인 스핀락(Spinlock) 을 사용하기도 한다.

스핀락은 하나의 프로세서에서 하나의 변수에 공유자원의 진입을 나타내는 플래그를 세팅하여

다른 프로세서에서 이 공유자원의 코드 부분에 진입하지 못하고 루프만 돌게함으로써 동기화를 맞출 수 있다.

따로 프로세서의  ContextSwitching 이 일어나지 않기 때문에 오버헤드가 적다.

ex)

long lock {0};

void SpinLockEnter()
{
	while(InterlockedExchange((Long*)lock, 1)) == 1);
}

void SpinLockLeave()
{
	InterlockedExchange((Long*)lock, 0));
}

 

[참고]

https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-interlockedexchange

 

InterlockedExchange function (winnt.h) - Win32 apps

Sets a 32-bit variable to the specified value as an atomic operation.

learn.microsoft.com

 

 

  • Mtex (뮤텍스)

[참고]

https://learn.microsoft.com/en-us/windows/win32/sync/mutex-objects

 

Mutex Objects - Win32 apps

A mutex object is a synchronization object whose state is set to signaled when it is not owned by any thread, and nonsignaled when it is owned.

learn.microsoft.com

https://learn.microsoft.com/en-us/windows/win32/sync/using-mutex-objects

 

Using Mutex Objects - Win32 apps

You can use a mutex object to protect a shared resource from simultaneous access by multiple threads or processes.

learn.microsoft.com

 

뮤텍스는 다음과 같은 사실들을 기억 하자.

 

  1. 뮤텍스 제어권을 통해 공유 자원 접근을 제어한다.
  2. 제어 방식은 FIFO 형식이라고 장담할 수 없다.
  3. 자신이 소유한 쓰레드가 누구인지를 기억한다.
  4. 같은 스레드가 같은 뮤텍스를 중복 호출하더라도 데드락이 발생하지 않는다.
  5. 뮤텍스를 갖고 있던 스레드(혹은 프로세스)가 강제 종료될 경우 뮤텍스 제어권을 반환 해준다.

 

 

1. 뮤텍스 제어권을 통해 공유 자원 접근을 제어한다.

 Each thread must wait for ownership of the mutex before it can execute the code that accesses the shared resource.

https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitforsingleobject

 

WaitForSingleObject function (synchapi.h) - Win32 apps

Waits until the specified object is in the signaled state or the time-out interval elapses.

learn.microsoft.com

뮤텍스 제어권을 통해 스레드 혹은 프로세스는 공유 자원 접근을 제어한다.

이 때 제어권 통제를 위해 WaitForSingleObject 혹은 WaitForMultipleObject 함수를 사용한다.

최초 뮤텍스는 Signaled 상태로, WaitForSimgleObject 같은 대기 함수를 호출함으로써 NonSignaled 상태로 바꾼다.

그 후에 접근 요청을 하는 스레드/프로세스들은 해당 뮤텍스가 NonSignaled 상태이므로 대기 상태가 된다.

 

그 후 ReleaseMutex 같은 함수를 사용하여 뮤텍스를 원래 상태인 Signaled 로 바꾸게 되면,

대기 중이던 스레드/프로세스들이 실행된다.

 

 

2. 제어 방식은 FIFO 형식이라고 장담할 수 없다.

If more than one thread is waiting on a mutex, a waiting thread is selected. Do not assume a first-in, first-out (FIFO) order. External events such as kernel-mode APCs can change the wait order.

이 때 대기 스레스/프로세스 우선순위는 FIFO(first-in, first-out) 라고 장담할 수 없다.

 

 

3. 자신이 소유한 쓰레드가 누구인지를 기억한다.

4. 같은 스레드가 같은 뮤텍스를 중복 호출하더라도 데드락이 발생하지 않는다.

After a thread obtains ownership of a mutex, it can specify the same mutex in repeated calls to the wait-functions without blocking its execution. This prevents a thread from deadlocking itself while waiting for a mutex that it already owns.

뮤텍스는 '자신이 소유한 쓰레드가 누구인지를 기억하고 있다.'

(참고 'Windows 구조와 원리 그리고 Codes')

때문에 같은 스레드가 같은 뮤텍스를 중복 호출하더라도 데드락이 발생하지 않는다.

(학생 때는 '같은 스레드가 같은 뮤텍스를 왜 중복 호출하지 ㅋㅋ' 라는 감상도 있었는데,

실제 개발하다보면 그런 경우가 비일비재하다...

각 기능들을 함수화 하고, 해당 함수들이 꼭 lock 으로 보호되어야 하는 상태에서 함수끼리 호출하다보면....

같은 스레드에서 같은 lock 을 호출하기도 한다 ㅋㅋㅋ...)

 

 

5. 뮤텍스를 갖고 있던 스레드(혹은 프로세스)가 강제 종료될 경우 뮤텍스 제어권을 반환 해준다.

If a thread terminates without releasing its ownership of a mutex object, the mutex object is considered to be abandoned. A waiting thread can acquire ownership of an abandoned mutex object, but the wait function will return 
WAIT_ABANDONED
 to indicate that the mutex object is abandoned.

즉, 스레드 A 가 뮤텍스를 소유, B 스레드가 뮤텍스 소유를 위해 대기 중인 상태에서 A 스레드가 강제 종료된다면 어떻게 될까.

최악의 경우 B 스레드가 무한정 대기할 수도 있겠지만,

다행히도  Windows 운영체제는 뮤텍스가 반환되지 않은 상태에서 스레드가 종료될 경우 강제적으로 Signaled 상태로 해줌으로써 대기 하던 스레드들이 무한정 대기 상태로 있는 것을 방지해준다.

또한 WaitForSingleObject 함수가 return 값으로 WAIT_ABANDONED 를 전달함으로써 비정상 상황임을 알려준다.

 

 

  • Critical Section (크리티컬 섹션)

[참고]

https://learn.microsoft.com/en-us/windows/win32/sync/critical-section-objects

 

Critical Section Objects - Win32 apps

A critical section object provides synchronization similar to that provided by a mutex object, except that a critical section can be used only by the threads of a single process.

learn.microsoft.com

A critical section object provides synchronization similar to that provided by a mutex object, except that a critical section can be used only by the threads of a single process. Critical section objects cannot be shared across processes.

크리티컬 섹션은 동일 프로세스(Process) 내에서만 사용할 수 있는 제약을 갖고 있다.

동작은 뮤텍스랑 비슷한데, 내부 구조가 더 단순해서 동기화 처리할 때 속도가 조금 더 빠르다.

 

 Unlike a mutex object, there is no way to tell whether a critical section has been abandoned.

다만 바로 위에서 Mutex 는 이게 문제가 생긴 상태인지 아닌지를 알 수 있는 방법이 있었는데, 크리티컬 섹션은 알 수 있는 방법이 읎다....

 

When a thread owns a critical section, it can make additional calls to EnterCriticalSection or TryEnterCriticalSection without blocking its execution. This prevents a thread from deadlocking itself while waiting for a critical section that it already owns. 

다행히도 뮤텍스와 마찬가지, 같은 스레드가 lock 중복 호출하더라도 데드락에 걸리는 일은 없다.

 

  • Semaphore (세마포어)

[참고]

https://learn.microsoft.com/en-us/dotnet/api/system.threading.semaphore?view=net-7.0 

 

Semaphore Class (System.Threading)

Limits the number of threads that can access a resource or pool of resources concurrently.

learn.microsoft.com

Limits the number of threads that can access a resource or pool of resources concurrently.

뮤텍스는 한 번에 하나의 쓰레드만이 접근할 수 있었는데, 세마포어는 사용자가 지정한 개수만큼 공유 자원에 접근할 수 있다.

(그래서 세마포어로 RW(read-write)lock 을 구현하기도 하는 듯. 더 자세한 내용은 다음에.)

지정 개수는 세마포어 초기화 시에 세팅한다.

해당 값이 1 이상이면 세마포어는 Signaled 상태이다.

해당 값이 0이면(더 이상 접근 불가) NonSignaled 가 되어 그 후에 접근하는 스레드들은 대기 상태가 된다.

 

 

[참고]

https://seokbeomkim.github.io/posts/locks-in-the-kernel-1/

 

리눅스 커널 락 종류 (1/5)

일상 속 글쓰기

seokbeomKim.github.io

https://drdbg.com/107

 

[WIN32] CRITICAL_SECTION 사용법 및 문제점

CRITICAL_SECTION 사용법 및 문제점 우리는 프로그램을 작성할 때 다중 THREAD에서의 객체 접근을 보호 하기위한 방법으로 CRITICAL_SECTION을 많이 사용한다. 사용법은 아래와 같다 . 1 2 3 4 5 6 7 8 9 10 11 12 13

drdbg.com

 

다음 글에서는 lock 심화 단계(?) 를 정리해보겠다.

'이것 저것 정리' 카테고리의 다른 글

Lock 심화(1) - RW Lock  (0) 2023.02.05
커널 레벨 스레드 vs 유저 레벨 스레드  (2) 2023.01.07