[Python] 동시성과 병렬성

파이썬은 병렬성을 제대로 지원하지는 않는 언어다. 최근 들어서 지원하려고 시도를 하고는 있지만, 지금 당장은 없는게 사실이다.

파이썬에 스레드가 없던건 뭔가 기술적인 한계가 있어서 그런건 아니고, 그냥 본인들이 만들기 어려우니까 안한거였다. 대충 간편하게 만들고 싶으니까 GIL이라고 해서 그냥 선 그어놓고 버려둔 것에 가깝다.

그래도 어느정도 에둘러서 병렬처리를 어떻게 할 수는 있다. 여기선 그 방법이나 구조에 대해서 다룬다.




threading 모듈을 통한 동시성 처리

이름 때문에 착각할 수도 있는데, 이건 동시성 처리를 위한 그린 스레드, 가짜 스레드일 뿐이다.
실제 OS 스레드를 호출하지 않기 때문에 진짜 병렬처리를 기대할 수는 없다. 소프트웨어 수준의 시분할 처리일 뿐이다.

아래는 threading을 사용한 간단한 코드 예제다.

import threading

counter = 0

def main():

    def run_thread1():
        global counter
        counter += 1
        print("Hello")
        print(counter)

    def run_thread2():
        global counter
        counter += 1
        print("World")
        print(counter)

    t = threading.Thread(target=run_thread1, args=())
    t2 = threading.Thread(target=run_thread2, args=())

    t.start()
    t2.start()

    t.join()
    t2.join()

if __name__ == "__main__":
    main()

스레드를 띄우고, 카운터 변수를 공유해서 출력한다.

근데 아무리 동시에 실행되는 것처럼 보여도 단일코어를 나눠쓰는 것이기 때문에, 뭔가 성능향상을 기대하면 안된다. 그걸 원한다면 다른 방법을 사용해야 한다.




multiprocessing 모듈을 통한 병렬 처리

스레드 사용이 불가능하니 OS 프로세스로 땜빵하자는 PHP식 사고에서 나온 병렬처리 방법론이다.
OS 프로세스에서 결국 OS 스레드를 호출하니 병렬이 되긴 한다...

그런데 인터페이스가 영 구리다. 기본적으로 그냥 파이썬 프로세스를 몇개씩 띄우는 것이기 때문이다.
게다가 C 인터페이스도 좀 섞어써야 한다.

아래는 기존 예제에 multiprocessing을 적용한 버전이다.

from multiprocessing import Process, Value
import ctypes

def run_process1(counter):
    with counter.get_lock():  # Acquire the lock before modifying the counter
        counter.value += 1
    print("Hello")
    print(counter.value)

def run_process2(counter):
    with counter.get_lock():  # Acquire the lock before modifying the counter
        counter.value += 1
    print("World")
    print(counter.value)

def main():
    counter = Value(ctypes.c_int, 0)  # Create a shared counter

    p1 = Process(target=run_process1, args=(counter,))
    p2 = Process(target=run_process2, args=(counter,))

    p1.start()
    p2.start()

    p1.join()
    p2.join()

if __name__ == "__main__":
    main()

대체적인 틀은 거의 같지만, 다른 점이 좀 있다. 일단 이건 진짜 완전 별도의 OS 프로세스이기 때문에 단순 코드 수준에서는 프로세스 간 변수 공유가 불가능하다.
그래서 Value 기능을 사용해서 특수한 공유용 변수를 할당하고, 프로세스 함수 파라미터에 넘겨서 그걸 공유하게 해야 한다.




FFI를 통한 간접적 병렬처리

그냥 그런 부분들을 C/C++나 Rust를 활용해서 구현하고 붙이는 방법도 있다.
하도 이런거로 고통을 겪는 사람이 많다보니, 이런 인터페이스도 괜찮게 되어있는 편이다.

https://blog.naver.com/sssang97/223072977222



참조
https://docs.python.org/ko/3/library/threading.html
https://docs.python.org/ko/3/library/multiprocessing.html#module-multiprocessing