이 포스트는 널널한 개발자님의 강의를 듣고 작성한 글입니다.
프로세스든 스레드든 여러개가 있다고 가정하자. 그런데 여기서 문제가 되는 건 '동시' 에 어느 한 대상에 접근하면 이 때 필연적으로 나타나는 현상이 경쟁조건(race-condition)이다. 아무튼 프로세스가 되었던 스레드가 되었던 여러개가 동시에 하나의 자원에 접근했을 때 이슈가 나는 것이다.
예를 들어 전역변수로 예금변수 선언 후 10만원으로 초기화했다고 가정해보자. 다른 한쪽에서는 10만원을 증가시키는 로직이 있고 다른 한 쪽에서는 10만원을 감소시키는 로직이 있다고 해보자. 그럼 여기서 봐야할께 10만원을 증가시키는 이 로직은 예금이라는 변수를 읽어오는 명령어 + 10만원을 더해주는 명령어 + 다시 예금이라는 변수에 overwrite하는 명령어 총 3개의 명령어로 구성되어 있다. 10만원을 감소시키는 로직도 이와 마찬가지이다. 그런데 여기서 CPU는 한번에 명렁 1개씩만 처리하기 때문에 원자성이 보장되지 않으면 뭔가 처리할때 다른게 끼어들 수 있다. 즉, 내가 생각하는 결과와 다르게 나올 수 있다. 자세히 보면 이 증가로직과 감소로직을 각각의 별도의 스레드로 처리한다고 했을 때 증가로직에서 10만원을 더해주는 명령까지 하고 OS가 suspend를 시켰다고 할때 감소로직에서 10만원을 감소시키는 로직이 수행되었고 그 이후에 증가로직의 마지막 명령 overwrite가 진행되었다고 하면 10만원 감소로직으로 0원을 기대했던 사용자는 20만원이 있는거에 감짝 놀랄 것이다.
공유자원이란 우리가 쉽게 생각해서 메모리나 파일정도로 볼 수 있다. 그래서 이런 동시성 이슈에 대해서 원자성을 부여함으로써 동시사건이 안되게 함으로써 예상 이슈를 차단할 수 있다.
그러면 중요한 것이 어떤 연산구문 n개가 있다고 가정하면 이 구문중에 전체에 대해 원자성을 보장한다고 보면 A라는 코드와 B라는 코드가 있다고 가정하고 각각 별도 스레드로 처리한다고 했을 때 A 전체 코드에 대해 원자성을 부여하면 A코드는 코드 시작전에 Lock을 걸어 CPU의 코어가 몇개든 간에 한 개의 스레드로 처리될 것이고 끝날 때 Unlock을 한다. 만약 Lock을 안하면 원자성이 손상될 우려가 있다.
임계구간이란 Critical Section으로 말 그대로 위험구간인데 중요한 것은 무엇을 임계구간으로 정하는것이냐이다. 즉, 무엇을 어디서부터 언제 어느 조건으로 임계구간으로 설정해야 하냐는것인데 이것은 완벽히 경험에 근간한다. 그런데 하나 확실 한것은 이 임계구간을 최소화해야 한다. 왜냐하면 이 구간이 길어지면 길어질수록 효율도 안 좋아지고 또 다른 문제인 DeadLock을 야기할 수 있다. 즉, 임계구간을 없앨 수 있으면 없애는게 좋고 이 말을 고급지게 Lock-free라고 한다.