콘텐츠로 건너뛰기
Home » 동기화

동기화

asyncio의 동기화수단들

asyncio는 단일 스레드에서 비동기 코루틴을 사용하여 동시성 처리를 한다. 따라서 asyncio의 세계에서는 적어도 멀티 스레드에서 발생할 수 있는 자원 선점문제가 없을 것이라 생각할 수 있다. 전적으로 틀린 것은 아니다. 스레드가 1개밖에 없기 때문에 메모리 내의 특정한 객체를 동시에 액세스하는 일은 없을 것이다. 그러나 그외의 IO와 관련된 자원은 여전히 선점 문제가 발생할 수 있다. 이러한 문제를 피하기 위해서 asyncio는 threading과 유사한 동기화 수단들을 제공하고 있으며, 이들의 사용 방법 또한 거의 유사하다. asyncio에서 제공하는 동기화 수단에는 다음과 같은 것들이 있다.

  • 락(Lock)
  • 이벤트(Event)
  • 컨디션(Condition)
  • 세마포어(Semaphore)
  • 바운디드세마포어(BoundedSemaphore)
더 보기 »asyncio의 동기화수단들

컨디션을 통한 스레드 동기화 예제

동시성을 다룰 때 여러 스레드가 하나의 자원에 순차적으로 접근하게 하거나, 반대로 특정한 시점에 동시에 작동하도록 하는 등의 상황에 제대로 대응할 수 있도록 락이나 이벤트와 같은 동기화 수단을 사용한다. 컨디션은 컨디션 락이라고도 하는데, 간단히 말하자면 이벤트와 락을 적절히 결합한 것이다. 락이나 R락을 사용하는 경우, 락을 획득한 구간의 코드는 항상 하나의 스레드만 진행할 수 있다. 락을 사용하는 중간에 다른 스레드로 사용 권한을 넘기려 한다면 현재 획득한 락을 해제하여 크리티컬 구간을 끝내야 한다. 그런데, 경우에 따라서는 atomic한 자원을 사용하려는 구간에서 해당 자원이 준비되지 않아 구간 내에서 일시적으로 실행을 중단하고 대기해야 하는 상황이 될 수 있다. 이런 경우에 컨디션 락을 사용한다.

컨디션은 락을 획득한 구간내에서 락을 일시적으로 반환하고 기다리는 역할을 가능하게 한다. 컨디션 락을 기다리는 스레드는 락을 해제하면서 블럭된다. 그러면 같은 컨디션락을 획득하려는 다른 스레드에게 제어권이 넘어가는데, 이러한 스레드 중에 해당 자원을 생성하는 역할을 담당하는 스레드가 있다면 이 생산자 스레드는 자원을 생성한 후, 이벤트와 비슷하게 대기 중인 다른 스레드를 깨워주게 된다. 생산자 스레드가 락을 반환하고 나면 중단됐다가 깨어난 스레드는 락을 다시 획득해서 해당 자원을 독점적으로 사용할 수 있게 된다.

중요한 것은 컨디션락은 락의 한 종류이기 때문에 대기중인 상태에서 깨어난 스레드는 그 즉시 실행을 재개하는 것이 아니라, 해당 락을 선점한 다른 스레드가 (이 스레드가 자신을 깨워주었을 것이다.) 락을 반환한 후 다시 그 락 객체를 획득해야 실행을 재개할 수 있게 된다는 것이다.

더 보기 »컨디션을 통한 스레드 동기화 예제

Lock을 사용하는 스레드 동기화 방법

아래는 어떤 “counter”라는 자원을 두 스레드가 동시에 사용하려할 때, Lock을 사용하는 상황을 시각적으로 묘사한 것입니다. 두 워커 스레드 A, B 는 자원에 접근하기 전에 Lock을 획득하려고 시도합니다. 두 스레드 모두 락 객체의 .acquire()를 호출합니다. 이 때 (아마도 간발의 차이로) A 가 락을 획득하게 되었다고 가정하면, A에서 호출한 .acquire()는 즉시 리턴되어 A는 다음 코드를 진행하게 되고 여기서 counter를 사용합니다. 반면 B의 .acquire() 호출은 락을 획득할 때까지 대기하기 때문에 B의 진행 흐름은 여기서 멈추게 되고, A가 자원을 쓰는 동안 . . .… 더 보기 »Lock을 사용하는 스레드 동기화 방법

스레드의 시작 시점을 동기화하기

동시성 프로그래밍에서 동기화는 주로 한정된 자원을 두고 여러 스레드가 경쟁하지 않도록 락이나 세마포어를 사용해서 특정한 자원을 액세스하는 시점에서는 여러 스레드가 순차적으로 실행하도록 하는 것에 초점을 맞추고 있다. 하지만 이 외에도 각각의 스레드가 각자가 담당한 작업을 처리하기 위해 준비를 마치고, 다른 스레드의 준비를 기다렸다가 동시에 시작하도록 하는 기법도 필요하다. 이렇게 여러 스레드를 특정한 지점에서 기다리게 한 후 한 번에 깨워서 동시에 시작하게 하는 용도로 사용되는 동기화 프리미티브로는 이벤트와 배리어가 있다.

이런 기법이 가장 흔히 사용되는 경우로는 소켓 서버와 클라이언트를 하나의 스크립트에서 구현해서 스레드로 돌게 할 때이다. 서버의 소켓이 준비되기 전에 클라이언트들이 서버에 connect 될 수 없기 때문이다.

이벤트는 가장 단순한 동기화 프리미티브 중 하나로, 동시에 시작해야 하는 여러 스레드들이 “출발선”에서 이벤트 객체의 .wait() 메소드를 호출하고 대기상태에 들어가도록 한다. 그리고 어느 한 스레드에서 해당 이벤트 객체의 .set() 을 호출하면 해당 이벤트를 대기 중인 모든 스레드에서 wait() 메소드가 리턴되면서 각 스레드가 동시에 시작될 수 있다.

배리어도 비슷하게 여러 스레드를 기다리게하다 한 번에 깨우는 장치인데, 마치 정원이 다 차면 바로 출발하는 버스처럼 작동한다. 즉 이벤트를 기다리는 스레드들은 누군가가 깨워줘야 하는 것에 비해, 배리어 정해진 개수만큼의 스레드가 대기하게 되면 자동으로 해제되면서 동시에 깨어나게 된다.

더 보기 »스레드의 시작 시점을 동기화하기

파이썬에서 스레드 사용하기 – threading

스레드는 프로그램이 실행되는 실행 흐름의 최소 단위이다. 어떤 프로그램이 실행되면 기본적으로 해당 프로그램을 위한 프로세스가 생성된다. 그리고 다시 이 프로세스는 하나의 스레드를 만들고 (이것이 해당 프로세스의 메인 스레드가 된다.) 이 스레드를 따라 코드가 실행된다.

하나의 프로세는 한 개 이상의 스레드를 동시에 실행시킬 수 있다. 이 말은 메인 루틴이 진행하는 동안 병렬적으로 다른 함수들이 같이 실행될 수 있다는 말이다. 스레드는 프로세스에 종속되므로 프로세스 내에서 스레드가 추가로 만들어질 때 이 새로운 스레드는 프로세스의 코드와 메모리를 공유한다. (반대로 멀티프로세스는 각각 독립된 코드 및 메모리 영역을 가지고 돌아간다.) 스레드는 이처럼 동시에 같은 작업들을 처리하여 전체적인 성능을 향상시키거나 루틴의 흐름을 중단시키지 않고 별개의 작업 흐름이 서브 루틴을 실행하여 서로 다른 작업을 함께 진행할 때 사용한다.

파이썬에서 스레드를 사용할 수 있도록 해주는 모듈로 _threadthreading이 있다. _thread 모듈은 저수준의 API를 제공하고 있고, 이를 기반으로 고수준 API를 제공하는 threading 모듈이 있다. 이 글에서는 threading 모듈을 사용하여 스레드를 생성, 실행하고 락, 세마포어 등의 동기화 수단을 사용하여 실행 흐름을 제어하는 방법을 살펴보겠다.

더 보기 »파이썬에서 스레드 사용하기 – threading

20090325 :: 폴더 동기화 유틸리티

외부에서 일하기

직업적 특성상 외근이 잦아, 각 종 문서 작업 들을 외부에서 해야하는 경우가 많이 있습니다. 예전에는 집-회사만 왔다 갔다 했던 경우여서, 작업하던 파일을 ‘서류가방’에 담아서 이동식 디스크로 동기화하여 사용하곤 했었는데요. 요즘에는 노트북을 통째로 들고서 움직이면서, 동시에 관리해야하는 폴더는 프로젝트 별로 여러 개를 동시에 왔다 갔다 하는 경우가 많아 이동식 디스크를 사용하기가 참 곤란하더군요. 결정적으로 ‘동기화’를 위해서는 사무실에 들어가서 USB 메모리를 PC에 꽂아주어야 하는 불편함도 있습니다.
그래서 보통은 ‘원격 데스크톱 접속’을 많이 사용하게 되었습니다. 이렇게 되면 모든 데이터가 사무실 PC에 저장되는 장점은 있지만, 모든 어플리케이션도 PC에서 구동, 작업을 해야하므로 작업 능률이 현저히 떨어지는 문제가 있습니다. 그리고 많은 경우에는 원격지에서 일을 할 때에도 네트웍을 제대로 쓸 수 없는 경우가 많지요. 결국 일을 하다보면 노트북에서 여러 자료를 작성하게 됩니다. (사무실 PC는 버려지는 분위기로 남게 되지요.)더 보기 »20090325 :: 폴더 동기화 유틸리티