Thinking Different




 스레드는 어떠한 프로그램 내에서, 특히 프로세스 내에서 실행되는 흐름의 단위를 말한다. 일반적으로 한 프로그램은 하나의 스레드를 가지고 있지만, 프로그램 환경에 따라 둘 이상의 스레드를 동시에 실행할 수 있다. 이러한 실행 방식을 멀티스레드라고 한다.

 

Spawn 으로 스레드 생성

Rust 에서는 새로운 스레드를 생성하기 위해서 thread::spawn 함수를 호출하고 여기에 우리가 실행하기 위한 코드를 작성할 수 있습니다.

use std::thread;
use std::time::Duration;

fn main() 
{
    thread::spawn(|| {
        for i in 1..10 {
            println!("생성 스레드 : {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("메인 스레드 : {}", i);
        thread::sleep(Duration::from_millis(1));
    }
}


// 실행 결과
메인 스레드 : 1
생성 스레드 : 1
생성 스레드 : 2
메인 스레드 : 2
생성 스레드 : 3
메인 스레드 : 3
메인 스레드 : 4
생성 스레드 : 4
생성 스레드 : 5

 

하지만 위 코드에서는 메인 스레드가 종료되면 생성된 스레드가 끝까지 실행되는 것을 보장받지 못하고 종료되게 됩니다.

 

이를 해결하기 위해서 Join 핸들 코드를 작성하여 스레드의 실행 결과를 보장 받을 수 있습니다.

 

use std::thread;
use std::time::Duration;

fn main() 
{
    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("생성 스레드 : {}", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("메인 스레드 : {}", i);
        thread::sleep(Duration::from_millis(1));
    }

    // 스레드가 종료될때까지 대기
    handle.join().unwrap();
}


// 실행 결과
메인 스레드 : 1
생성 스레드 : 1
생성 스레드 : 2
메인 스레드 : 2
메인 스레드 : 3
생성 스레드 : 3
메인 스레드 : 4
생성 스레드 : 4
생성 스레드 : 5
생성 스레드 : 6
생성 스레드 : 7
생성 스레드 : 8
생성 스레드 : 9

맨 처음 코드와 다르게 실행결과를 보면 생성 스레드의 실행 결과를 보장 받을 수 있다.

 

 

move 클로저

move 클로저는 어떤 스레드의 데이터를 다른 스레드에서 사용 가능하도록 해주는 기능이다.

 

다음 코드를 보면 메인스레드에서 생성된 data라는 변수를 생성된 스레드에서 출력하는 코드이다. 하지만 여기서는 에러가 발생된다. 소유권 규칙이 존재하기 때문이다.

use std::thread;

fn main() 
{
    let data = 10;

    let handle = thread::spawn(|| {
        println!("생성 스레드 : {}", data);
    });

    // 스레드가 종료될때까지 대기
    handle.join().unwrap();
}

 

 

여기서 move 클로저를 사용하여 소유권을 넘겨주면 문제없이 작동된다.

use std::thread;

fn main() 
{
    let data = 10;

    let handle = thread::spawn(move || {
        println!("생성 스레드 : {}", data);
    });

    // 스레드가 종료될때까지 대기
    handle.join().unwrap();
}


// 실행 결과
생성 스레드 : 10

 

메시지패싱을 사용한 스레드간 데이터 전송

메시지 패싱을 사용하면 스레드간의 안전성을 확보하면서 데이터를 전송할 수 있다. 이를 가능하게 하는 것이 채널이라는 것인데 mpsc::channel() 을 사용하여 송신, 수신 채널을 생성하여 스레드간에 데이터를 전송할 수 있는 방법이다.

 

use std::thread;
use std::sync::mpsc;

fn main() 
{
    // 송신, 수신 채널 생성
    let (sender, recver) = mpsc::channel();

    thread::spawn(move || {
        let val = String::from("Hello World!");
        // 송신자로 데이터를 전송한다.
        sender.send(val).unwrap();
    });

    // 수신자로 데이터를 전송 받는다
    let received = recver.recv().unwrap();
    println!("받은 데이터 : {}", received);
}

// 출력 결과
받은 데이터 : Hello World!

 

'프로그래밍 언어 > Rust' 카테고리의 다른 글

[Rust] 26. 싱글 스레드 웹서버  (0) 2023.04.06
[Rust] 25. 스레드 동기화  (0) 2023.03.24
[Rust] 23. 파일 입출력  (0) 2023.03.10
[Rust] 22. 커멘드라인 아규먼트  (0) 2023.03.09
[Rust] 21. 테스트 코드  (0) 2023.03.06