스레드의 동기화 문제와 해결법

이원석·2022년 4월 5일
2

OS

목록 보기
2/8

1. 동기와 비동기란


동기(Synchronous)란 다수의 개체들이 동일한 무언가를 가지는 것입니다. 무언가가 동일하게 되는것을 말합니다. 그 무언가는 상태나 행위, 시간, 속도, 주기, 출현 등이 될 수 있습니다.

OS에서의 동기는 두 개의 프로세스가 서로 데이터를 주고받을 때, 주고 받는 순서(또는 시간)가 일정하다는 뜻입니다.

동기적이다

어떤 작업을 요청했을 때 그 작업이 종료될 때까지 기다린 후 다음 작업을 수행합니다.

데이터를 주고받는 순서가 중요할 때 사용되며, 요청한 작업만 처리하면 되기 때문에 전체적인 수행 속도는 빠를 수 있습니다.

하지만, 한 작업에 대한 시간이 길어질 경우, 전체 응답이 지연될 수 있습니다.



비동기적이다

어떤 작업을 요청했을 때 그 작업이 종료될 때까지 기다리지 않고(작업을 위임하고), 다음 작업을 수행합니다. 요청했던 작업이 끝나면 결과를 받고, 그에 따른 추가작업을 수행합니다.

따라서 요청한 순서에 상관없이, 동시에 다수의 작업을 처리할 수 있지만 위임한 작업이 끝날때 따로 이벤트를 감지하고 결과를 받아 추가 작업을 해야하기 때문에 비교적 느릴수 있습니다.

(I/O 작업이 잦으며 빠른 응답속도를 요구하는 프로그램에 적합 합니다.)





2. 스레드의 동기화 문제


동기화 문제는 멀티 스레드가 프로세스 내의 같은 자원을 공유함으로써 발생하게 됩니다. 따라서 이러한 문제를 방지하기 위해 스레드들에게 하나의 자원에 대한 처리 권한을 주거나 순서를 조정하는 기법을 사용합니다 (동기화 시킨다).



2-1. Thread-safe

Thread-safe란 직역 그대로 스레드 안전입니다. 멀티 스레드 환경에서 여러 스레드가 동시에 하나의 자원(객체 및 변수) 에 접근할 때, 기법을 활용하여 의도한 대로 동작하는 상황을 뜻합니다.


Thread-safe하게 구현하기

Thread-safe 하기 위해서는 공유 자원에 접근하는 임계영역(Critical Section)을 동기화 기법으로 제어(상호배제)해줘야 합니다.


Re-entrant

Re-entrant는 재진입성이라는 의미로, 어떤 함수가 Re-entrant 하다는 것은 여러 스레드가 동시에 접근해도 언제나 같은 실행 결과를 보장한다는 의미입니다.
(Re-entrant 하다면 Thread-safe 하지만 역은 성립하지 않는다)


int var = 1

inf f()
{
	var = var + 2;
    return var;
}

스레드가 함수 f()를 실행하여 var에 동시접근할 경우, var는 전역 변수이므로 결과가 실행 시점에 따라 바뀌게 됩니다. 따라서 f()는 재진입성을 가지지 않습니다.



inf f(int i)
{
	int var = i;
	var = var + 2;
    return var;
}

이렇게 매개변수를 전달받아 결과를 반환하도록 함수를 고치게 되면 재진입성을 보장합니다.



2-2. 스레드 동기화 방법

  1. 실행 순서의 동기화
    • 스레드의 실행 순서를 정의하고, 이 순서에 반드시 따르도록 하는 것

  1. 메모리 접근에 대한 동기화
    • 메모리 접근에 있어서 동시 접근을 막는 것
    • 실행의 순서가 중요한 상황이 아니고, 한 순간에 하나의 스레드만 메모리에 접근하면 되는 상황을 의미





3. 동기화 기법


유명한 Toilet Problem 예제를 통해 이 둘을 간단하게 비교할 수 있습니다.

3-1. 뮤텍스(Mutex)

공유된 자원의 데이터를 여러 프로세스나 스레드가 접근
하는 것을 막는 것

임계영역을 가진 스레드나 프로세스의 Running Time 이 서로 겹치지 않도록 각각 단독으로 실행하는 기법입니다.


뮤텍스는 화장실이 하나밖에 없는 식당과 비슷합니다. 화장실을 가기 위해서는 카운터에서 열쇠를 받아가야 합니다.

다른 사용자들은 열쇠가 없으면 화장실을 갈 수 없으며 카운터에서 대기합니다. 화장실을 이용을 끝낸 사람이 카운터에 키를 반납하게 되면 카운터에서 대기하던 다음 사용자가 키를 가지고 화장실을 사용할 수 있습니다.


여기서 화장실은 공유자원, 화장실 키는 공유자원에 접근하기 위한 오브젝트, 사람들은 프로세스 혹은 스레드 입니다.

즉 뮤텍스는 화장실 키에 해당하는 오브젝트가 있으며 이 오브젝트를 소요한 자만이 공유자원에 접근할 수 있습니다.




3-2. 세마포어(Semaphore)

공유된 자원의 데이터에 대한 최대 허용치 값을 두어 
프로세스나 스레드의 동시 접근을 것을 막는 것

공유 리소스에 접근할 수 있는 프로세스의 최대 허용치만큼 동시에 사용자가 접근하여 사용할 수 있습니다.

각 프로세스는 세마포어 값을 확인하고 변경할 수 있습니다. 세마포어를 사용하는 프로세스는 그 값을 확인하고, 자원을 사용하는 동안에 그 값을 변경함으로써 다른 세마포어를 사용하는 프로세스나 스레드들이 기다리게끔 해야합니다.


세마포어는 여러개의 화장실이 있는 식당과 비슷합니다. 화장실에는 여러개의 칸이 있으며 화장실 입구에는 현재 화장실의 빈 칸 개수를 보여주는 전광판이 있습니다.


모든 화장실칸에 사람이 들어갈 경우 빈 화장실칸은 0 이 됩니다. 빈 칸의 개수가 1이 될 때까지 다음 사용자는 기다려야 합니다.


여기서 화장실이 공유자원이며 사람들이 스레드 혹은 프로세스 입니다. 그리고 화장실 빈 칸의 개수(설정한 한계치) 는 현재 공유자원에 접근할 수 있는 스레드와 프로세스의 개수를 나타냅니다.





[참고문헌]
https://ko.wikipedia.org/wiki/%EC%9E%AC%EC%A7%84%EC%9E%85%EC%84%B1
https://velog.io/@chy0428/OS-%EB%A9%80%ED%8B%B0%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EB%A9%80%ED%8B%B0%ED%94%84%EB%A1%9C%EC%84%B8%EC%8B%B1
https://github.com/WeareSoft/tech-interview/blob/master/contents/os.md#thread-safe

0개의 댓글