TECH 으로 돌아가기
TECH HACKER NEWS 오늘 6분 읽기 31 READS

동시성·상호작용성·가변성: 셋 중 둘만 고를 수 있다

흔히 겪는 장면 하나로 시작해 보자. 커먼 리스프로 짠 서버가 돌아가고 있다. 여러 스레드가 네트워크 연결을 받고, 다른 스레드들은 뒤에서 데이터를 처리한다. 데이터 접근 충돌이 없도록 공들여 설계했으니 동시성은 확보됐다. 이맥스와 SLIME으로 실행 중인 서버에 붙어 상태를 들여다볼 수 있으니 상호작용성도 있다. 전역 해시 테이블에 잘못된 값이 들어간 걸 발견하지만, REMHASH 한 줄이면 지울 수 있으니 가변성도 있다. 그런데 하필 그 폼이 평가되는 순간 다른 스레드가 같은 해시 테이블을 읽는다. 그것으로 끝이다. 이 짧은 사고 실험이 원문 글의 출발점이며, 저자는 여기서 세 가지 속성을 동시에 완전하게 누리는 것이 왜 근본적으로 어려운지를 짚는다.

세 가지 욕심의 정체

동시성은 '같은 시간 단위 안에서 여러 실행 스레드가 진행되는 것'으로, 더 빠르게 더 많은 데이터를 처리하려는 욕심에서 나온다. 대가는 복잡성이다. 교착 상태, 기아, 메모리 손상을 피하려면 데이터 접근을 일일이 보호해야 한다. 상호작용성은 실행 중인 프로그램의 상태를 언제든 들여다보고 고칠 수 있다는 편의에서 나온다. 대가는 위험이다. 아무 데이터나 아무 때나 만질 수 있다는 말은, 동시에 다른 스레드가 읽고 쓰는 데이터를 건드릴 수 있다는 뜻이다. 가변성은 그저 실용적이기 때문에 원한다. 문제는 이 셋이 한자리에 모이면 서로를 무너뜨린다는 점이다.

무엇을 버릴 것인가

가장 흔한 선택은 상호작용성을 버리는 것이다. 실행 중에 데이터를 무작위로 건드리는 사람이 없으면 그로 인한 동시성 문제도 없다. C, 러스트, 고가 이 길을 택했다. 효율적이고 안전하지만, 실행 도중 시스템과 대화하는 능력을 잃는다. 개발 중에도 답답하고, 재시작이 늘 가능하지는 않은 복잡한 시스템을 디버깅하고 고칠 때는 특히 아쉽다. 스레드는 여러 개 돌릴 수 있지만 프로그램 안 무엇도 동시에 읽거나 쓸 수 없다.

두 번째 길은 동시성을 실질적으로 포기하는 것이다. 파이썬과 루비가 GIL(전역 인터프리터 락)로 택한 방식이다. 스레드는 여럿 돌지만 파이썬·루비 데이터에 대한 모든 접근이 전역 락으로 보호된다. 덕분에 파이썬 셸이나 루비의 Pry에서 데이터를 안전하게 읽고 쓸 수 있다. 대신 느리다. 대부분의 동시 프로그램은 전체 접근 중 극히 일부에만 동기화가 필요한데, 동기화 자체가 느리기 때문에 그 범위를 일부러 좁게 잡는다. 그런데 모든 접근을 락으로 감싸면 그 비용을 고스란히 치러야 한다.

세 번째 길은 가변성을 손보는 것이다. 변수를 읽을 때 런타임이 값을 안전하게 복사해서 돌려주고, 스레드끼리도 값의 복사본을 주고받으며 통신한다. 어디에 있는 값이든 직접 고칠 수는 없지만, 메시지 기반이므로 메시지를 보내는 방식으로 언제든 안전하게 상태를 갱신할 수 있다. 얼랭이 택한 방식이다. 프로세스라 부르는 스레드가 공유 메모리 대신 복사된 값을 담은 메시지로 통신하니 동시성이 안전하고, 시스템과의 상호작용도 자유롭다. 종이 위에서는 많은 문제가 풀린다. 그러나 복사는 느리다. 매우 느리다. 데이터 표현을 최적화하고 이진 덩어리는 참조 카운팅으로 복사를 피할 수 있지만, 그래도 근본적인 비용은 남는다.

실무자가 읽어야 할 지점

이 글이 한국 실무자에게 주는 메시지는 언어 선택이 곧 트레이드오프 선택이라는 오래된 진실을 세 축으로 또렷하게 세워준다는 데 있다. 성능이 중요한 프로그램은 대개 상호작용성을 버려 공유 데이터 접근을 최대한 줄이고, 상호작용의 편의를 원하는 쪽은 전역 락이나 불변 데이터 모델이 부과하는 추가 비용을 감수한다. 어느 쪽도 공짜가 아니다. 그래서 기술 스택을 고를 때 '이 언어가 무엇을 잘하는가'만큼이나 '무엇을 포기하도록 설계됐는가'를 함께 보는 편이 낫다. 운영 중 라이브 디버깅이 얼마나 중요한지, 처리량이 얼마나 절실한지, 상태를 얼마나 자주 바꿔야 하는지에 따라 답이 갈린다.

커먼 리스프 개발자들은 셋을 다 가질 수 있다고 자랑할 것이다. 실제로 REPL에 붙어 살아 있는 시스템을 들여다보고 고치는 경험은 다른 생태계가 쉽게 흉내 내지 못한다. 다만 글이 마지막에 못박듯, 그 자유에는 조건이 붙는다. REPL에서 하는 어떤 작업이든 돌아가는 프로그램을 깨뜨릴 수 있다는 사실을 늘 염두에 둬야 한다. 결국 '셋 다'는 언어가 강제로 막아주지 않는 대신 그 책임을 개발자에게 넘긴 형태다. 도구가 대신 지켜주지 않는 안전을 사람이 규율로 메워야 한다는 점에서, 이 자유는 곧 부담이기도 하다.

SOURCE · HACKER NEWS
원문 전체 보기 → https://www.n16f.net/blog/concurrency-interactivity-mutabili...
SHARE
처리 중...