프로그래밍 언어/Python

GIL를 무조건 신뢰하면 안되는 이유( feat.스레드 감수성 )

JMDev 2023. 9. 15. 18:45

나는 생각했다.

그러면 GIL 때문에 쓰레드 작업에서 락걸거나, 따로 통제할 필요가 없겠네.

GIL 특성상 무조건 딱 하나의 스레드만 실행시켜주니깐!

하지만 이 말은 틀렸다.

GIL는 동시접근을 보장해주는 락역활이라고 생각했지만 그렇지않다.

그 이유는 코드로 확인해보자.


class Counter:
    def __init__(self):
        self.count = 0

    def increment(self, offset):
        self.count += offset

def worker(sensor_index, how_many, counter):
    BARRIER.wait()
    for _ in range(how_many):
        counter.increment(1)

# Example 3
from threading import Barrier
BARRIER = Barrier(5)
from threading import Thread

how_many = 10**5
counter = Counter()

threads = []
for i in range(5):
    thread = Thread(target=worker,
                    args=(i, how_many, counter))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

expected = how_many * 5
found = counter.count
print(f'Counter should be {expected}, got {found}')

위 코드를 쭉 읽어보면 스레드에 연산할 수 있는 갯수까지 정의해주었다.

그러면 GIL이 당연히 락역활을 해주니깐 순차적으로 진행해서 정답은 500000(10**5 * 5) 이겠네? 라고 생각할 거고,

이론적으로도 그게 맞다. 하지만 정답은 예상할 수 없다이다.

고개가 갸우뚱.. 이 문제를 이해하기 위해선 스레드 감수성이 필요하다

 

스레드는 이 코드를 만나고

self.count += offset

를 통해 count를 증감시키고 있었다. 하지만 위 코드의 연산은 실제로 이렇게 작동된다.

value = getattr(counter, 'count')
result = value + 1
setattr(counter, 'count', result)

나는 분명 self.count += offset에 오면 종료할 수 있게 설계되었다구!

그래서 종료를 했는데 하필이면 result = value+1 까지 왔을 때, 스레드가 스위칭이 되었다.

즉, 스레드의 컨디션이 좋으면 전부 실행되지만, 컨디션이 안좋다면 전부 실행하지 않게 될 수 있는 것이다.

 

이를 보완하기 위해 Lock 클래스를 사용하여 상호배제 락을 확실히 이행할 수 있다.

from threading import Lock

class LockingCounter:
    def __init__(self):
        self.lock = Lock()
        self.count = 0

    def increment(self, offset):
        with self.lock:
            self.count += offset

BARRIER = Barrier(5)
counter = LockingCounter()

for i in range(5):
    thread = Thread(target=worker,
                    args=(i, how_many, counter))
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

expected = how_many * 5
found = counter.count
print(f'Counter should be {expected}, got {found}')