자료구조 시간에 heap은 Priority Queue라고 배웠습니다.
그럼 파이썬 라이브러리에서 이 차이점은 뭘까?
차이점을 알기 위해서는 선행되어 알아야 하는 개념 2가지가 필요합니다.
1) GIL
2) thread-safe, Thread-Non-Safe
# 선행되는 개념보고 다음글로 넘어가기
👉 👉 https://codewizard.tistory.com/53
[Python] Priority Queue, heapq의 차이점
자료구조 시간에 heap은 Priority Queue라고 배웠습니다. 그럼 파이썬 라이브러리에서는 2개 다 존재하는데 이 차이점은 뭘까? 내부로직은 heapq를 사용하고 있었다. 그럼 차이점은 뭘까? https://stackoverf
codewizard.tistory.com
# GIL(Global Interpreter Lock)
파이썬은 GIL을 적용하고 있다.
GIL은 파이썬에서만 존재하는 독특한 개념으로
파이썬에서 멀티스레딩을 할 때
다수의 스레드가 동시에 파이썬 바이트 코드를 실행하지 못하게 막는 일종의 뮤텍스(Mutex)입니다.
즉, 멀티스레딩을 하더라도 여러 스레드가 동시에 작업하지 않습니다.
<일반적인 멀티스레드> | <파이썬 GIL 멀티스레드> |
![]() |
![]() |
=> GIL이 스레드끼리 공유하는 프로세스의 자원을 Global 하게 Lock 해버리고 단 하나의 스레드에만 이 자원에 접근하는 것을 허용합니다.
<GIL 멀티스레드를 사용한다면>
1) 스레드 간에 context switching 비용 발생(단점)
2) 멀티스레드가 오히려 성능이 낫거나 비슷한 결과 발생(단점)
그럼 파이썬은 GIL을 왜 사용할까요?
파이썬에 존재하는 모든 것은 객체이다.
1) 파이썬은 이러한 객체들을 Reference count(참조 횟수)를 저장하고 있다.
2) 이 값은 각 객체들이 참조되는 횟수고, 참조 여부에 따라 알아서 증감
3) 어떤 객체에 대한 모든 참조가 해제되어 Reference count가 0이 된다면, 파이썬의 GC(Garbage Collector)가 그 객체를 메모리에서 삭제
4) 따라서 Reference count가 항상 정확해야 GC가 객체를 처리가능
BUT
여러 스레드에서 동시에 한 객체에 접근하면 어떻게 될까요?
race condition이 Reference count의 대해서 발생할 수 있고, GC가 부적절한 처리를 야기함.
*참고: GIL은 CPython에서만 사용됨.
2023.08.09 기사에 의하면
https://www.itworld.co.kr/news/302737
파이썬에서 GIL 삭제된다⋯“병렬 처리의 혁신적 진전”
많은 논란 끝에 파이썬 운영 위원회(Python Steering Council)가 “C파이썬에서 전역 인터프리터 잠금(Global Interpr
www.itworld.co.kr
앞으로 no-GIL을 선택사항으로 두기 위해 PEF 703( no-GIL)을 긍정적으로 검토. (향후 몇 년 후에 사라질 수 있음.)
https://peps.python.org/pep-0703/
그럼 GIL이 있어도 Lock이 필요할까?
값을 재사용하는 트랜잭션이라면 race condition이 발생한다.
lock은 성능을 현저히 떨어뜨린다.
단순하고 계산위주의 작업일수록 single-thread 가 낫다.
파이썬 데이터 타입의 thread-safe라는 말은 무슨 뜻인가?
앞서 말했듯 Python 은 멀티스레딩환경에서 공유메모리(heap)에 접근하는 스레드가 유일하도록
GIL을 이용해 제한하고 있다.
ex) list타입은 append, pop 등의 메소드를 가지며 heap메모리에 값을 읽고 쓰는 작업을 수행한다.
list객체가 수행하는 작업에 대해 atomic 하게 보장한다는 뜻이 thread-safe이다.
여러 스레드가 동일한 heap영역을 읽고 쓰기를 한다 해도 자신의 객체에 대한 메모리(r/w) 작업에 대해 안전함을 보장
한다는 뜻이다. (파이썬은 내부적으로 GC가 항상 수행되고 있기 때문에, 싱글스레드작업이라 해도 멀티스레드 형태로 수행됨.)
여기서
# Atomic이란?
Atomic이란 "분할 불가능한"이라는 의미로, 여러 스레드에서 동시에 접근하는 공유 데이터의 일관성을 유지하기 위한 도구다. Atomic 연산은 All-Or-Nothing 원칙을 따르는데, 이는 연산이 완전히 수행되거나, 아니면 전혀 수행되지 않아야 함을 의미한다. 이를 통해 멀티스레드 환경에서 공유 데이터를 안전하게 처리할 수 있다.
Atomic 객체는 원자적 연산을 제공한다. 이는 여러 스레드에서 동시에 접근해도 데이터의 일관성을 보장한다는 뜻이다. 하지만, 이러한 보장을 위해 필요한 동기화 과정 때문에 일반적인 연산에 비해 비용이 더 들 수 있다.
따라서 성능이 중요한 상황에서는 Atomic 연산의 사용에 주의해야 한다.
그래서
thread-safe, Thread-Non-Safe는 무엇일까?
# thread-safe, Thread-Non-Safe
1) thread-safe
- 공유 자원에 대한 안전한 접근이 보장되는 코드
2) thread-non-safe
- 공유 자원에 대한 적절한 동기화가 이루어지지 않은 코드
- race condition 발생할 수 있음.
여기까지 길게 설명했는데 핵심을 요약해보면
# 요약
- GIL은 파이썬 객체의 접근을 보호하기 위한 뮤텍스
- CPython은 C언어가 바탕이고 따라서 메모리 관리 정책도 C를 따른다. C에서 thread를 사용할 때에 메모리관리정책은 사용자의 몫으로 넘겨져 있다.(free(), foo()등)
- 이때, CPython은 생성된 객체를 reference count로 세어가면서 메모리관리를 GC가 한다.
- 사용자에게 역할을 넘겼으므로 race condition발생가능.
- GIL이 mutex를 통해 모든 referfence개수를 일일이 보호하지 않고, 멀티스레드에서 Python interpreter를 잠그는 정책인 GIL 채택(Global Interpreter Lock)
- 멀티스레드에서 역할을 GIL에게 넘겨 thread-safe를 유지하도록 "안전"을 얻고 "성능"을 잃는 trade off 정책 실행 중(성능 잃는 이유: 스레드 간 context swiching 비용이 계속 발생.)
- CPU자원 처리 권한을 lock 하는 개념이기에 I/O bound( I/O 기기에 요청만 하고 결과를 받는 작업)만을 멀티스레드로 분리하여 처리하면 성능개선이 가능하지만 side-effect 발생할 수 있음.(이를 알고 큰 프로젝트에서 임해야 함.)
# 최종 요약
- 멀티스레드에서 GIL이 적용되어 thread-safe를 유지하며 "안전"을 얻고 trade off로 멀티스레드의 이점인 "성능"포기.
'Programming > Python' 카테고리의 다른 글
[Python] 파이썬의 함수 인자 전달 방식(Call by Assignment) (1) | 2024.05.23 |
---|---|
[Python] Priority Queue, heapq의 차이점 (0) | 2024.01.14 |
[Python] class와 self (0) | 2023.10.20 |
[Python] +, append(), extend() 사용법과 내부로직 (0) | 2023.10.17 |