프로

프로세스 에서 Elixir

5개의 연습 문제

프로세스 소개

Elixir에서는 모든 코드가 프로세스 안에서 실행돼요. Elixir의 프로세스는:

  • 시스템 프로세스와 혼동하면 안 돼요.
  • 가벼워요.
  • 구체적인 용도가 있어요. 다음과 같은 일을 할 수 있어요:
  • Elixir 앱이 수백 개의 프로세스를 실행하는 것은 자연스러운 일이에요. 그렇지만 특히 라이브러리라면 새로운 프로세스를 명시적으로 전혀 만들지 않는 앱도 흔해요.

프로세스 만들기

기본적으로 함수는 자신을 호출한 프로세스에서 실행돼요. 특정 함수를 새로운 프로세스에서 명시적으로 실행해야 할 때는 spawn을 사용해요:

  • spawn/1은 직접 실행할 함수를 받아요.

    spawn(fn -> 2 + 2 end)
    # => #PID<0.125.0>
    
  • spawn/3은 모듈 이름, 함수 이름(atom), 그리고 그 함수에 전달할 인자 목록을 받아 실행해요.

    spawn(String, :split, ["hello there", " "])
    # => #PID<0.113.0>
    
    • 이 세 가지 데이터 조합은 흔히 MFA(Module, Function, Arguments)라고 불러요.
  • 프로세스는 함수의 실행이 끝나는 즉시 종료돼요.

  • 프로세스가 아직 살아 있는지(실행 중인지)는 Process.alive?/1으로 확인할 수 있어요:

    pid = spawn(fn -> 2 + 2 end)
    Process.alive?(pid)
    # => false
    

메시지

프로세스는 서로 정보를 직접 공유하지 않아요. 프로세스는 데이터를 공유하기 위해 메시지를 보내요. 이런 동시성 패턴을 액터 모델이라고 해요.

  • 프로세스에 메시지를 보낼 때는 send/2를 사용해요.

    send(pid, :hello)
    
    • 메시지는 보낸 순서대로 수신자의 _메일박스_에 들어가요.
    • send는 메시지가 잘 도착했는지도, 수신자가 아직 살아 있는지도 확인하지 않아요.
  • 메시지는 어떤 타입이든 될 수 있어요.

  • 현재 프로세스로 보내진 메시지는 receive/1으로 받을 수 있어요.

    • 메시지에 패턴 매칭을 해야 해요.
    • receive는 주어진 패턴 중 하나라도 일치하는 메시지가 프로세스의 메일박스에 들어올 때까지 기다려요.
      • 기본적으로는 무한정 기다리지만, after 블록으로 타임아웃을 줄 수 있어요.
    • 읽은 메시지는 프로세스의 메일박스에서 제거돼요. 읽지 않은 메시지는 그곳에 무한정 남아 있어요.
      • 쌓여 있는 읽지 않은 메시지 때문에 메모리가 부족해지지 않도록, receive/1에는 항상 모든 경우를 처리하는 _ 절을 써요.
    receive do
      {:ping, sender_pid} -> send(sender_pid, :pong)
      _ -> nil
    after
      5000 ->
        {:error, "No message in 5 seconds"}
    end
    

수신 루프

메시지를 두 개 이상 받고 싶다면 receive/1을 재귀적으로 호출해야 해요. 흔한 패턴은 receive/1을 호출하고, 메시지로 무언가를 처리한 뒤, 다시 자신을 호출해 다음 메시지를 기다리는 재귀 함수를 구현하는 거예요. 이런 함수에는 보통 loop라는 이름을 붙여요. receive/1 호출 사이에 어떤 상태를 이어가야 한다면, 그 loop 함수에 인자를 넘겨서 할 수 있어요.

def loop(state) do
  receive do
    :increment_by_one ->
      loop(state + 1)

    {:report_state, sender_pid} ->
      send(sender_pid, state)
      loop(state)

    :stop ->
      nil

    _ ->
      loop(state)
  end
end

실제로는 이런 방식을 직접 쓰는 경우가 드물어요. Elixir는 수신 루프를 기반으로 만들어지는 동시성 추상화를 제공해요. 예를 들어 Agent 모듈이나 GenServer behaviour가 있어요. 그렇지만 이런 추상화를 효율적으로 사용하려면 그 기초를 이해하는 것이 아주 중요해요.

GitHub에서 편집 링크가 새 창이나 탭에서 열려요

프로세스 배우기

연습이 잠겨 있어요

프로세스 개념을 연습하려면 연습 문제 4개를 더 잠금 해제해요