중독과 욜로

술을 마실 때는 그 때만 즐거을 뿐이고, 게임을 할 때도 그 때만 즐거울 뿐이다. 숙취에 고통받을 때, 게임 종료 버튼 앞에 있을 때, 우리는 다시 허무함을 느낀다.

쾌락의 순간에는 더 큰 역치를 넘어서지 못하는 유한함에 부딪힌다. 허무감인지, 어떤건지.

친구 중 한 명의 생활 방식은, 요즘 말하는 욜로(YOLO) 에 근접한(?) 삶을 산다고 느껴지는데, 이유는 이렇다. 일단 불안한 계약직인데다 계약 텀도 굉장히 짧은 직종에 근무한다. 그런데도 잘 놀러 다닌다. 너무나도. 그러면서 좋은 남자 만나 결혼해야지, 정규직도 되면 좋겠지, 계속 놀러 다니고는 싶지… 이런, 쓰고 보니 이 친구는 욜로가 아니다. 내일 살 걱정을 하기는 하니까.

중독은 종합해보면, 더 큰 보상을 요구하는 뇌 어딘가의 장난질이자, 그 외에 할 수 있는 일을 하나씩 배제시키는 습관의 힘이 합쳐진 것처럼 보인다. 올바른 (적어도 사회적으로 올바르다고 여겨지는) 성취의 경우, 보상은 즉각적이지 않다. 사실 그 위치를 전이시켜줄 힘을 기르는 과정임에도, 올챙이적 기억이 리셋되듯이 그런 지위를 당연시하는 것도 문제다. 하지만 게임 한 판 지면 열받고, 술이나 담배가 떨어지면 괜히 불안하다. 보상이 들어오지 않았으니까.

습관은 더 무서운데, 보상기제가 작동하건 말건 옵션은 항상 LRU 리스트처럼 관리된다는 것이다. 새로운 도전은 그만큼의 비용이 들지만, 곁에 둔 습관은 그렇지 않다. 초식이 그대로 작동해도, 레시피를 몸에 둘둘 말아둔 듯 아무런 힘도 들지 않는다. 반복적인 작업은 그것이 나쁘건 좋건 간에, 여유 시간에 ‘그냥 할 만한 것들’의 유력한 후보로 항상 존재할 것이다. 그 후보는 2선, 3선을 밥먹듯이 할테고.

과연, 여기까지 생각해볼 때 욜로는 중독과 관련이 있나. 소미의 옆집 아저씨도 오늘만 산다고 하는데, 욜로라고 하지 않는다. 다음을 걱정을 하는 자에게, 중독을 이어가는 이 허무함은 더 고통스럽지 않을까? 그래서 욜로는, 실제로 그런 극단적인 허무감을 회피하지 못한 비웃음의 단어는 아닌지, 아니면 정말 해탈했다고 자랑하고 싶은건지 의심스럽기만 하다.

암보스 (ambos)

두 여자가 서로 다른 방향을 보고 있지만, 그 얼굴 안에서는 마주보기도 하는 듯, 흑심을 품고 있는 듯한 이미지가 프랙탈처럼 나타나 있는 기괴한 표지에 담긴 내용은 대체 어떤 사연일까?

출판사 ‘황금가지’에서 만든 새로운 라인업 ‘수상한 서재’ 시리즈의 첫 작품인 김수안 작가의 암보스를 읽어봤다. 시간이 없어 서울-대구를 오가는 KTX 안에서 읽었는데, 다소 두꺼운 외형과는 달리 꽤나 빨리 따라갈 수 있었다.

암보스 (ambos) 는 스페인어로 ‘양쪽, 두 사람’ 이란 뜻이다. 두 여자가 주인공인 것을 표현하고자 했겠으나, 실제로 스페인어는 남성형/여성형 명사가 따로 존재한다. 그래서 표지만 보고는 왜 암바스 (ambas) 라는 여성형 명사를 채택하지 않았을까 자뭇 궁금해졌다. arm boss 같은 느낌도 있어서

줄거리

이하는 최대한 스포일러를 피해서 나온 (그래서 책 소개내용과 거의 흡사한) 초반 상황 요약이다.

신문 기자 이한나 는 어느 날 방화사건 현장에 있었고, 목격한 모든 정보를 회사에 전달한 뒤 의식을 잃었다. 이대로 죽는건가… 아니, 죽는 것도 나쁘지 않겠지. 무능하고 철면피인 아버지가 진 빚이며, 헤어진 남자친구며, 내가 잘못 굴린 펜으로 사람이 곤란에 겪었으니까.

깨어나보니, 이한나는 다른 사람이 되어 있었다. 같은 날 옥상에서 뛰어내렸지만 가까스로 목숨을 건졌던 강유진이란 사람으로. 이한나는 퇴원하자 마자 강유진의 집을 찾아갔는데, 별안간 이한나의 모습을 한 누군가가 뒤따라 찾아왔다. 그는 강유진 이었다.

몸이 뒤바뀐 것이다. 어떻게 된 일일까.

초자연은 중요한 게 아니다

하지만 책은 왜 이들의 몸이 바뀌었는지는 독자들에게 이야기해주지 않는다. 누군가가 상대방의 몸을 원했다면, 영화 ‘더 게임’ 의 강회장 (변희봉 扮) 같은 캐릭터가 나와야 하겠지만, 여기선 어느 누구도 그런 역을 자처하지 않는다. 그보다는, 서로의 삶에서 느끼는 ‘잃은 것과 얻은 것의 의미’를 알아가고 행동하는 데에 많은 부분을 할애한다. 강유진은 비만에 집에 틀어박혀 지내기 일쑤지만 돈이 많았고, 이한나는 예쁜데다 활기차고 자기주관이 강했지만 안하무인 아버지로 인해 많은 빚을 졌다.

설마 강유진의 모습을 한 이한나가 ‘나는 열심히 운동해서 살을 빼야지’ 라거나 ‘이제부터 사람들과 잘 어울려야지’ 같은 뭐 이런 희망적인 스토리를 기대하지는 말자. 그들은 언젠가 다시 본래의 상태로 돌아갈 것이라고 확신하고 있었다. 정확히 말하자면 강유진이 그렇게 될 것이라고 이야기했지만. 아무튼 그렇게 돌아가버린다고 가정했을 때 이들은 어떤 행동을 취할까. 상대방의 미스터리한 행적이 서로의 시선을 통해 서술되는 그 순간.

갑자기 교차하는 사건

연쇄살인사건, 그리고 그 범인을 찾는 형사가 교차되어 나타난다. 일면 관련없어 보이는 사건 이야기가 갑자기 주인공 일행과 교차점을 지나면서 충돌하기 시작한다. 그것도 아주 빠르게. 파열음은 의외로 강하고, 당사자들의 추리 게임은 꼬리에 꼬리를 문다.

형사 측 인물에서 가장 눈에 띄는 것은 단연 박선호 형사일 것이다. 우락부락한 체격과 어울리지 않게 집요하리만치 파고드는 집중력이 소설 내내 돋보인다. 그 옆의 부사수 칠범 역시 파트너로서의 역할에 충실한다. 이한나의 가족과 주변인, 강유진의 증언 등으로 그 사건 이후 사람이 달라졌다는 부분을 끝까지 물고 늘어지는데, 이 부분에서 주인공 일행과의 긴박한 밀당이 계속 이뤄진다. 결국 살인사건은 실마리를 찾고 해결되지만… 이게 정말 끝일까?

이야기 하는 방식

소설이 가지는 강점은 심리 묘사와 비유에 많은 에너지를 쏟았고 그걸 고스란히 전달하려고 노력했단 점이다. 사건의 진위가 아니라, 사건에 휘말린 인물의 세세한 면면을 나타내려고 애를 썼다. 그래서 스토리 자체에 태클을 걸면서 본다면 자칫 넘어지기 쉬울 것 같아 보이긴 하지만, 그런 세세한 부분을 너그러이 이해해준다면 재밌게 읽힐 소설이지 않을까.

작중 이한나의 시점, 박선호 (를 포함한 외부)의 시점에서는 이한나와 강유진을 지칭하는 자아가 서로 다른 것이 신선했다. 이한나의 시점에서 서술될 때는 ‘나’ 와 ‘(내 모습을 한) 강유진’ 이지만, 그 외에는 외모대로 ‘강유진’ 과 ‘이한나’ 로 서술된다. 박선호가 이를 눈치챈 종반부에서는 서술이 다시 뒤바뀌긴 하지만. 그래서 이 부분을 따라가기가 조금 피곤해 질 수는 있겠다.

마치며

독자에게 추리할 여지를 많이 주는 것 같지만, 사실 복선은 야속하게 정류장을 지나치는 시내버스 같이 지나간다. 어느샌가 소설 속 인물들의 추리보다 한발 앞서 나간게 아닐까, 그랬던 거였어! 라고 생각하고 있다면, 조심해야 한다. 끈적한 손으로 뒤통수를 후려갈겨서 뒷맛이 찜찜하다. 이게 뭐야, 꼭 그렇게 했어야만 했냐! 같은 느낌. 하지만, 역으로 생각해보면 책을 한번 더 돌려보게 만드는 매력을 지니고는 있다고 볼 수 있다.

소설 중에 이런 내용이 있다. 강유진의 모습을 한 이한나가 창문을 바라본다. 창문에는 강유진이 보인다. 내가 정말 나인지, 상대방이 내 모습을 하고 유리창에 나타난건지, 정말로 상대방이 내가 된건지. 나는 누구일까. 사람의 몸이 뒤바뀐다는 초자연적인 전개에만 관심을 가지지 말고, 상대방의 거죽을 쓰고 자신도 몰랐던 민낯을 드러내는 속물같은 추악함도 기대해 보자.

Atomic Operation 으로 하는 동시성 제어

Test-And-Set (TAS)

TAS 를 이용해서 간단한 동시성 제어를 할 수 있다. testAndSet 이라는 function 을 가지고 아래의 do...while 문을 쓰레드 A, B 에서 동시에 호출한다고 해 보자. 이 때 lock 은 같은 변수이다.

function TestAndSet(boolean_ref lock) {
    boolean initial = lock
    lock = true
    return initial
}

do {
    while(TestAndSet(&lock))
        ; // do nothing
    // critical section
    lock = false;
    // remainder section
} while(true);

우선 TestAndSet() 은 다음과 같은 일을 한다. 말 그대로 ‘지금 값이 무엇인지 검사하고, 값을 바꾼다’ 는 것이다.

  1. lock의 현재 값을 저장해 둔다.
  2. lock의 값을 true 로 설정한다.
  3. 저장한 lock 의 값을 반환한다.

그럼 이걸로 어떻게 아래 block 의 critical section 에 대한 동시성 제어를 할 수 있을까? Thread A 가 먼저 실행했다고 가정하면, 이런 시나리오가 된다.

  1. A : TestAndSet() 의 반환값이 false 이다. while 문을 빠져나온다.
  2. B : TestAndSet() 의 반환값이 true 이다. (A가 true로 두고 나왔기 때문에) while 문에서 계속 돈다.
  3. A : Critical Section 수행 후, lock 을 false 로 바꾼다.
  4. B : 여러 번의 TestAndSet() 호출 후에, 드디어 반환값이 false 가 되었다
    (A가 false 로 두고 나왔기 때문에) while 문을 빠져나온다.

자, 그런데 뭔가 이상하다. 이렇게 이상적으로 동작하지 않을 것 같다. TestAndSet() 함수를 라인별로 동시에 실행한다고 하면 이런 사단이 날 수 있다.

  1. A : TestAndSet() 에 진입해 lock 값을 저장한다. 이 값은 false 이다.
  2. B : TestAndSet() 에 진입해 lock 값을 저장한다. 이 값은 false 이다.
  3. A : TestAndSet() 에서 lock 값을 true 로 바꾼다.
  4. B : TestAndSet() 에서 lock 값을 true 로 바꾼다.
  5. A : TestAndSet() 에서 저장한 값을 반환한다. 이 값은 false 이다.
  6. B : TestAndSet() 에서 저장한 값을 반환한다. 이 값은 false 이다.
  7. A & B : 모두 동시에 critical section 을 수행한다.

그럼 어떡하나? TestAndSet() 은 그래서 저런 함수만으로는 안 되고 Test-And-Set 의 연산이 일관되도록 조정해야 한다. 함수 안에 spinlock 을 쓰면 되겠네요? 싶겠지만 lock 구현하자고 lock 을 또 만드는 건 아닌 것 같다. 그래서 Test-And-Set 은 CPU에서 지원하는 Atomic Instruction 을 사용한다.

Fetch-And-Add : Ticket Lock

Atomic Operation 으로 구현할 수 있는 Lock 중에 Ticket Lock 이 있는데, Fetch-And-Add 로 구현할 수 있는 방법을 알아보자.

ticketLock_init(int *next_ticket, int *now_serving)
{
    *now_serving = *next_ticket = 0;
}

ticketLock_acquire(int *next_ticket, int *now_serving)
{
    my_ticket = fetch_and_inc(next_ticket);
    while(*now_serving != my_ticket) {}
}

ticketLock_release(int *now_serving)
{
    now_serving++;
}

TAS 의 케이스를 이해하고 본다면 별 다른 설명이 필요 없을 것 같다.

  1. Table Lock 초기화를 한다.
  2. A : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (0) 과 now_serving (0) 이 같은 값이므로 곧바로 빠져나온다.
  3. B : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (1) 과 now_serving (0) 이 다른 값이므로 while 문에서 대기한다.
  4. C : Table Lock 을 얻으려 한다. 이미 얻었던 my_ticket (2) 과 now_serving (0) 이 다른 값이므로 while 문에서 대기한다.
  5. A : Table Lock 을 해제한다. now_serving (0) 을 증가시켜 now_serving (1) 을 만든다.
  6. B : 비로소 Table Lock 을 얻었다. (C는 여전히 대기 중이다.)

여기서 핵심은 fetch_and_inc 인데, 마찬가지로 얻어오는 루틴과 값을 증가시키는 루틴이 따로 떨어져 있으면 중복된 티켓을 들고 기다리는 쓰레드들이 발생할 수 있다. 따라서 이것도 atomic operation 이 되어야 한다.