[Blocking vs Non-Blocking] 왜 헷갈릴 수밖에 없는가 - 애매성에 대한 언어학적 해설

[Blocking vs Non-Blocking] 왜 헷갈릴 수밖에 없는가 - 애매성에 대한 언어학적 해설

서론

BlockingNonBlocking의 개념에 대해 익히 공부하셨을 겁니다. 보통 작업이 완료될 때까지 제어권을 반환하는가로 구분됩니다. 호출한 작업이 끝날 때까지 호출한 쓰레드는 멈추게 된다고도 해설하죠.

다만 아래 함수는 Blocking Function 일까요?

class MyClass {
  void myFunction() {
    for(int i = 0; i < 100_000_000; i++) {
      // DO_NOTHING
    }
  }
}

함수는 작업이 완료될 때 까지 호출자에게 제어권을 반환하지 않습니다. 이 경우 블로킹 함수라 정의할 수 있죠. 다만, 함수는 작업이 완료될 때 까지 스레드제어 아래에 있습니다. 이 경우 스레드는 단순히 Busy하게 작업을 진행할 뿐, OS에게 제어권을 주지 않죠. 그렇다면 이 함수는 과연 블로킹 함수라 부를 수 있을까요?

결론부터 말하지면, Blocking Function 이라는 명사구는 생략(Ellipsis)으로 인한 애매성(Ambiguity) 를 지녀 다의적으로 해석될 수 있습니다. 화용론적으로 Blocking Function의 등장 상황에서는 일반적으로 생략된 논항에 대한 추론을 할 수 있는 맥락이 주어집니다.

한국에서 흔히 찾아볼 수 있는 정의로는 대상이 “Caller” 이기에, 한국의 담화 맥락에서는 이 함수는 Caller를 blocking한다고 부를 수는 있겠으나 엄밀한 답은 아닙니다.

Predicate “BLOCK”

Blocking Function에서 Blocking은 block이라는 서술어의 분사형용사구 형태입니다. block이라는 서술어와 프로그래밍에서 blocking function에 대해 의미론, 화용론적으로 접근해 보도록 하겠습니다.

의미론적 접근

block이라는 서술어는 2-place-predicate 또는 3-place-predicate로, 각각 2개 또는 3개의 논항(argument) 이 필요합니다. 의미론의 semantic roles(theta role)에 따르면,

2-place:block(x,y)x=agent (행동을 의도적으로 수행하거나 원인이 되는 실체)y=theme 또는 patient (차단당하는 대상 또는 상태가 변하는 대상)3-place:block(x,y,z)z=Source 또는 Path (대상이 차단되는 경로나 막고 있는 장소)\begin{aligned} &\text{2-place:}\quad block(x, y) \\ &\quad x = \text{agent (행동을 의도적으로 수행하거나 원인이 되는 실체)} \\ &\quad y = \text{theme 또는 patient (차단당하는 대상 또는 상태가 변하는 대상)} \\ \\ &\text{3-place:}\quad block(x, y, z) \\ &\quad z = \text{Source 또는 Path (대상이 차단되는 경로나 막고 있는 장소)} \end{aligned}

즉, block이라는 동사는 최소한 Agent(행위자)Theme / Patient(피행위자) 라는 핵심적인 의미역을 요구합니다.

그런데 blocking function이라는 용어는 표면적으로 오직 1 argument(즉, function 그 자체)만 명시합니다. 즉,

blocking function    block(function,  ?)\text{blocking function} \implies block(\text{function}, \ \ ?)

여기서 누구를 blocking 하는가(theme/patient) 에 대한 논항이 생략되어 있으므로, 결국 의미론적으로 block(agent, ?)block(agent, \ ?)와 같이 불완전한 서술어 구조가 됩니다. 이 생략된 자리는 문맥(pragmatics)에 따라 caller, thread, event loop 등으로 다의적 해석이 발생할 수밖에 없고, 바로 이 불완전성 때문에 “blocking function” 용어가 영원히 혼동될 수밖에 없는 참조 애매성을 가집니다.

화용론적 접근

프로그래밍 컨텍스트에서, 별도의 IO나 Thread 명시가 없으면, 생략된 themecaller로 유추하는 것이 가장 적합해보입니다.

다만 사용 사례에 따라 theme을 Thread로 보는 경우도 상당히 잦습니다. 다들 헷갈려 하는 모습이죠.

실제 영어권 원문을 보면 항상 “blocking” 이라는 표현을 해설하기 위해 무엇이 theme인 지 명시되거나 맥락이 존재합니다.

Node.js 공식 문서

“blocking methods execute synchronously and block the event loop until the operation completes” → theme = event loop

In Node.js, JavaScript that exhibits poor performance due to being CPU intensive rather than waiting on a non-JavaScript operation, such as I/O, isn’t typically referred to as blocking.

특히 위에서 이야기 했던 CPU Intensive한 작업은 Node.js 컨텍스트에 한해서 Blocking 작업으로 정의되지 않음을 명시적으로 밝힙니다.

GeeksforGeeks

“Blocking code stops everything else until it finishes” → theme = caller’s control flow

Kotlin 공식 문서 (Coroutines API)

“runBlocking blocks the current thread until its completion.” -> theme = thread

공식문서에서 blocking이라는 표현을 쓸 땐 함께 엄밀히 무엇을 블로킹하는 것인 지 자체를 명시적으로 정의하고 있습니다.


즉, blocking function은 화용적 맥락에 의해 뜻이 정의가 되기 때문에 맥락없이 무엇이 blocking인 지를 논하는 것 자체가 무용입니다.

결론

blocking function의 의미는

  • 의미론: theta role 불완전 (block(x,?)block(x, ?))
  • 화용론: theme이 누구인 지에 대한 맥락적 내포(implicature)

를 통해 언어학적으로 풀이할 수 있습니다. 즉 맥락에 따라 다의성을 지니기에 맥락 없이 엄밀한 정의 자체를 내릴 수 없는 표현입니다.

따라서 “블로킹이 도대체 뭔지” 엄밀히 정의하고자 노력하기 보단 애초에 용어 자체가 맥락에 따라 다의적으로 해석될 수 있는 용어구나 하고 넘어가는 걸 추천합니다.

실제 프로그래밍에서 나오는 다양한 자연어 용어들 또한 다의성을 지니는 경우가 많습니다. 이런 경우 개발자 본능으로 엄밀히 정의를 하려하기 보단 애초에 concrete한 정의가 불가능 한 것은 아닌 지 생각해보는 것도 좋을 거 같습니다.