[rust+react] 1. 계획 및 비동기 프로그래밍 개요

성욱김·2022년 2월 7일
0

백엔드로 rust 프론트엔드로 react.js 를 사용하여 웹페이지를 하나 만들어 볼 생각이다. rust 프레임워크로는 actix-web이라는 것을 사용할 계획이며,

우선 rust 먼저 공부 할 예정이다.

참고할 문서는
1. https://actix.rs/docs/application/
actix 메뉴얼
2. https://namsoocho.github.io/async-book/01_getting_started/01_chapter.html
rust 비동기 프로그래밍 관련 문서

순서는
actix-web 튜토리얼 -> react 튜토리얼 -> 이후 개인 프로젝트 진행이다.

actix-web 튜토리얼을 진행하던 도중 rust 기본 문법을 배울땐 등장하지 않았던 async/await 함수에 대해 알아보기 위해 잠깐 2번 문서를 공부하려 한다.

async와 await은 다른 프로그래밍 언어에서도 본 것 같다.

1. 비동기 방식과 사용하는 이유

한번에 여러 작업을 하기 위해서 보통은 thread를 사용하게 된다. 그러나 thread를 통해 코드를 작성하면 , thread사이에 데이터를 주고 받을 때 오버헤드가 발생하게 되며 이를 방지하는 방법이 비동기 코드를 작성하는 것이다.

1.1 비동기 방식의 효율성

간단한 예시를 들어보자.

노래 부르기 , 노래 배우기 , 춤추기 세가지의 활동이 있을 때

노래를 부르기 전에 배워야 한다는 것은 명확하지만 춤추는 것은 앞의 두가지 활동과 동시에 진행되어도 괜찮다.

이때 최고의 효율을 내기 위해서는

async fn learn_and_sing() {
    // Wait until the song has been learned before singing it.
    // We use `.await` here rather than `block_on` to prevent blocking the
    // thread, which makes it possible to `dance` at the same time.
    let song = learn_song().await;
    sing_song(song).await;
}

async fn async_main() {
    let f1 = learn_and_sing();
    let f2 = dance();

    // `join!` is like `.await` but can wait for multiple futures concurrently.
    // If we're temporarily blocked in the `learn_and_sing` future, the `dance`
    // future will take over the current thread. If `dance` becomes blocked,
    // `learn_and_sing` can take back over. If both futures are blocked, then
    // `async_main` is blocked and will yield to the executor.
    futures::join!(f1, f2);
}

fn main() {
    block_on(async_main());
}

코드를 위와 같이 작성해야 한다. async learn_and_sing 함수를 통해 배우기와 부르기의 순서 강제성을 만들고 , dance와는 함께 일어날수 있도록 해주는 것이다.

2. Future trait

rust의 비동기 프로그래밍은 Future trait을 놓고 이야기 할 수 없다.
trait이 무엇인지에 대한 복기는 따로 진행하지 않는다.

Future는 값을 생성할 수 있는 비동기 연산 이다.


trait SimpleFuture {
    type Output;
    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
}

enum Poll<T> {
    Ready(T),
    Pending,
}

사용자는 poll함수를 통해 Future를 실행할 수 있다.

이때 반환값은 Ready와 Pending으로
Future가 완료되면 --> Ready
Future가 완료되지 않았으면 --> Pending을 반환한다.

또한 Pending을 반환한 후에는 다시 Future를 진행할 준비가 되었을 때
wake() 함수를 호출해야 한다.

문서에서는 한가지 예로, 사용가능한 데이터가 있는지 명확하지 않은 소켓을 읽는 경우를 고려했다.

데이터가 있으면 Poll::Ready(Data)를 반환하고 없으면 futureblock된다.
이때 데이터가 다시 준비되면 호출할 wake()가 존재해야 한다.
(그렇지 않으면 언제 다시 시작할지 알 수 없다)

pub struct SocketRead<'a> {
    socket: &'a Socket,
}

impl SimpleFuture for SocketRead<'_>{
    type Output = Vec<u8>;

    fn poll(&mut self, wake: fn()) -> Poll<Self::Output>{
        if self.socket.has_data_to_read(){
            Poll::Ready(self.socket.read_buf())
        } else {
            self.socket.set_readable_callback(wake);
            Poll::Pending // wake 실행 및 Pending 반환
        }
    }
}

예시를 간단히 구현한 것이고
실제 Future trait은 다음과 같다.

trait Future {
    type Output;
    fn poll(
        // Note the change from `&mut self` to `Pin<&mut Self>`:
        self: Pin<&mut Self>,
        // and the change from `wake: fn()` to `cx: &mut Context<'_>`:
        cx: &mut Context<'_>,
    ) -> Poll<Self::Output>;
}

Pin, Context등 못보던 것들이 존재하지만 전체적인 흐름은 동일하다.

2.1 Future와 wake

실제 Future trait은 wake 대신 cx가 존재하는데 , 역할은 동일하지만 간단하게
poll과 wake 중간에 데이터를 저장하기 위해서 정도로 이해하고 넘어가자.

왜냐하면 일반적으로 처음 poll 되었을 때 모든 일을 끝마칠 수 없는 경우가 대부분이기 때문이다.

async와 await에 대한 대략적인 이해를 끝마쳤으므로,
본격적인 actix_web 문서에 대한 공부는 다음 장에 이어서 진행한다.

0개의 댓글