Web 접속 방법 Rust, Python 비교

러스트의 경우 처음에 명령 프롬프트 창에서 포트를 열어놓고 Cargo run을 하라고 하여 Python은 포트를 열 필요없이 잘 접속되는데 해서 비교하게 되었습니다. 그리고, 보면 파이썬은 편리한 상태를 만들어 놓았는데, Rust는 처음부터 내가 만들어가야 하는 상황입니다.

1. Rust를 이용한 웹 접속

가. 명령 프롬프트 방식

(1) 명령 프롬프트에서 프트 열기

chromedriver –port=9515를 실행해서 Chromedriver를 실행시킵니다.

명령 프롬프트에서 9515 포트 열기

Chromedriver가 Path에 있다면 그냥 실행하면 되는데, path가 설정되어 있지 않아, chromedriver.exe가 있는 폴더로 이동해서 실행했습니다.

(2) 브라우저 열기 Rust 코드

(가) Cargo.toml
[dependencies]
thirtyfour = "0.36.1"
tokio = { version = "1", features = ["full"] }

thirtyfour와 tokio 라이브러리를 가져와야 합니다.

(나) main.rs
use thirtyfour::prelude::*;
use tokio;

#[tokio::main]
async fn main() -> WebDriverResult<()> {
    // 이미 chromedriver가 9515 포트에서 실행 중이라고 가정
    let driver = WebDriver::new("http://localhost:9515", DesiredCapabilities::chrome()).await?;

    // 페이지 접속
    driver.get("https://www.rust-lang.org").await?;

    // 타이틀 가져오기
    let title = driver.title().await?;
    println!("Page title: {}", title);

    // 종료
    driver.quit().await?;
    Ok(())
}

코드이 내용은 port가 열려 있기 때문에 다시 열 필요는 없고, 9515 포트로 driver를 설정한 다음, rust-lang.org에 접속한 후 title을 가져와서 화면에 출력하는 것입니다.

문제 없이 코드가 실행되고, title이 표시됩니다.

cargo run을 이용해 rust-lang-org에 접속한 후 타이틀을 가져와 화면에 출력

나. 코드로 포트 열기 방식

명령 프롬프트에서 포트를 연 다음 Cargo run을 한다는 것이 이상하므로 Rust에서 포트를 열고, 실행하려면 아래와 같이 코드를 작성하면 됩니다.

Cargo.toml은 동일하고,

main.rs만 아래와 같이 수정하면 됩니다.

use std::process::Command;
use thirtyfour::prelude::*;
use tokio;

#[tokio::main]
async fn main() -> WebDriverResult<()> {
    // 실행파일이 있는 디렉토리 경로
    let exe_dir = std::env::current_exe()
        .unwrap()
        .parent()
        .unwrap()
        .to_path_buf();
    let chromedriver_path = exe_dir.join("chromedriver.exe");
    println!("chromedriver 경로: {}", chromedriver_path.display());

    // chromedriver 실행
    let mut child = Command::new(&chromedriver_path)
        .arg("--port=9515")
        .spawn()
        .expect("chromedriver 실행 실패");

    // WebDriver 클라이언트 연결
    let caps = DesiredCapabilities::chrome();
    let driver = WebDriver::new("http://localhost:9515", caps).await?;

    driver.get("https://www.rust-lang.org").await?;
    println!("현재 페이지 타이틀: {}", driver.title().await?);

    // 브라우저 닫기
    driver.quit().await?;
    // chromedriver 프로세스 종료
    child.kill().ok();

    Ok(())
}

use std::process::Command;가 추가되었습니다.

main에서 먼저 chromedriver_path를 설정하고,

Command::new를 이용해 포트를 연 다음 child에 저장하고,

caps는 python Selenium에서 사용하는 ChromeOptions 역할입니다. 기본적인 옵션만 설정하는 것입니다. caps.add_arg(“–headless”)?; 등을 추가해서 옵션을 추가할 수 있습니다.

그리고, WebDriver::new로 http://localhost:9515라고 9515포트를 이용해 localhost를 연 다음

driver.get로 https://www.rust-lang.org를 연 다음

driver.title().await?로 title을 가져와서 화면에 출력합니다.

아래는 Visual Studio Code에서 Run한 장면입니다.

Compile과 실행 잘 되고, ChromeDriver was started successfully on port 9515.와

현재 페이지 타이틀 : Rust Programming Language라고 잘 나옵니다.

9515 포트 열기와 rust-lang.org에 접속해서 title 가져오기를 통합한 실행 화면

2. Python을 이용한 웹 접속

가. chrome_connect.py

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import os

def main():
    # 실행 파일이 있는 디렉토리 기준으로 chromedriver.exe 찾기
    exe_dir = os.path.dirname(os.path.abspath(__file__))
    chromedriver_path = os.path.join(exe_dir, "chromedriver.exe")

    # chromedriver.exe 고정 경로
    # chromedriver_path = r"C:\android\chromedriver.exe"
    # print(f"chromedriver 경로: {chromedriver_path}")

    # chromedriver 실행 (포트 지정 없이 내부적으로 관리)
    service = Service(chromedriver_path)
    options = webdriver.ChromeOptions()

    driver = webdriver.Chrome(service=service, options=options)

    try:
        driver.get("https://www.rust-lang.org")
        print(f"현재 페이지 타이틀: {driver.title}")
    finally:
        driver.quit()

if __name__ == "__main__":
    main()

Python은 Cargo.toml과 같은 설정이 필수가 아니고,

바로 python code를 위와 같이 작성하면 됩니다.

실행 결과는 아래와 같습니다.

파이썬으로 chromedriver를 실행한 후 rust-lang.org의 타이틀을 화면에 출력

나. 코드 내용

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
import os

필요한 selenium과 os 라이브러리를 불러옵니다.

    # 실행 파일이 있는 디렉토리 기준으로 chromedriver.exe 찾기
    exe_dir = os.path.dirname(os.path.abspath(__file__))
    chromedriver_path = os.path.join(exe_dir, "chromedriver.exe")

    # chromedriver.exe 고정 경로
    # chromedriver_path = r"C:\android\chromedriver.exe"


실행 파일이 있는 폴더의 chromedriver.exe를 chromedriver_path로 설정합니다.

주석 처리한 것처럼 고정 경로로 지정할 수도 있습니다. 여러 프로그램에서 공통적으로 사용할 수 있으므로 PC에서만 작업한다면 이것이 편할 수 있습니다.

print(f"chromedriver 경로: {chromedriver_path}")

chromedriver_path를 화면에 출력합니다.

    # chromedriver 실행 (포트 지정 없이 내부적으로 관리)
service = Service(chromedriver_path)
options = webdriver.ChromeOptions()

Service 메소드를 이용해 service를 생성하고, webdriver.ChromeOptions()로 크롬 설정을 기본으로 합니다.

driver = webdriver.Chrome(service=service, options=options)

service와 options 설정으로 크롬을 열고, driver 객체에 담습니다.

    try:
driver.get("https://www.rust-lang.org")
print(f"현재 페이지 타이틀: {driver.title}")
finally:
driver.quit()

www.rust-lang.org 열기를 시도해서 성공하면 페이지의 타이틀을 driver.title로 가져와서 화면에 표시합니다.

그리고, 크롬을 종료합니다.

3. 러스트와 파이썬 비교

Rust는 Port를 반드시 지정해야 하는데, Python은 selenium을 이용하기 때문에 포트를 지정할 필요가 없는 차이점이 있습니다.

자세히 말하면 Rust의 thirtyfour는 W3C WebDriver 프로토콜을 따르는 라이브러리라서, chromedriver.exe를 서버처럼 띄우고 http://localhost:9515 같은 포트를 통한 HTTP 요청으로 제어하는데 비해

Python의 selenium은 내부적으로 chromedriver.exe를 subprocess로 실행하고, 외부에서 포트 번호를 직접 지정할 필요 없이 Selenium이 알아서 관리합니다.

4. 실행 파일 사이즈

pyinstaller -F -w chrome_connect.py로 실행한 후 파일 사이즈를 보면

18메가 정도되는데,

cargo run으로 생성한 실행 파일 크기는 8메가 정도되는데,

cargo build –release해서 만든 Rust 실행 파일을 보면 4메가 정도로 파이썬 18메가의 22%뿐이 안됩니다.

속도도 미세하지만 Rust가 빠른 듯 합니다.

Rust 까다롭고, 생소한 측면이 너무 많지만 좋기때문에 자꾸 익히고 적응해나가야 하겠습니다.

Offline PC 파이썬 환경 설정

파이썬은 관련 라이브러리를 온라인으로 가져와 설치하는데, Offline PC는 인터넷이 안돼서 포기하고 있었는데, 방법을 알게 되어 공유합니다. VS Code와 Python 설치, VS Code Extension과 파이썬 관련 라이브러리 설치 세 가지로 나뉩니다.

1. VS Code와 Python 설치 파일 다운로드 및 설치

가. 설치 파일 다운로드

설치를 해야 하니 온라인 PC에서 설치 프로그램을 다운 받아 오프라인 PC로 옮기로 실행하면 간단히 끝납니다.

VS Code 설치 사이트는 https://code.visualstudio.com/이고,

VS Code 다운로드 사이트

파이썬 설치 사이트는 https://www.python.org/downloads/입니다.

python download 사이트

나. 오프라인 PC로 전송

USB를 이용하던, 다른 전송 방법을 사용하던 설치 파일을 오프라인 PC로 옮깁니다.

다. 설치

VS Code 설치는 어렵지 않은데,

파이썬 설치시 주의할 점은 설치 초기 화면에 “Add Python.exe to PATH” 부분에 체크가 되어 있지 않은데, 반드시 체크하고 설치해야 한다는 점입니다. 물론 나중에 할 수도 있지만 설정 방법이 어렵기때문입니다.

python 설치 시 'Add python.exe to PATH' 설정 화면

2. VS Code Extension 설치

가. Python 익스텐션 다운로드

파이썬을 편리하게 사용하기 위한 최소 익스텐션은 Python입니다. 이건 파이썬 설치 파일이 아니므로 1번에서 설치하는 파이썬과는 다르며, 이것만 있다고 파이썬 실행이 되는 것이 아닙니다.

https://www.vsixhub.com/vsix/12/#google_vignette 사이트에서 파이썬 익스텐션 VSIX 파일을 다운로드 받을 수 있습니다.

위 사이트로 접속한 후 아래로 내려가면 ‘Download Latest VSIX File 버튼이 있으므로 이걸 눌러 익스텐션 설치 파일을 다운로드 받습니다.

VSIX 사이트의 'Download Latest VSIX File 메뉴

나. 오프라인 PC로 옮김

VS Code를 실행하고 왼쪽 아이콘에서 Extensions를 선택하고

Extensions: Marketplace 오른쪽의 점 3개를 누르면 맨 아래에 ‘Install from VSIX…’ 메뉴가 보이므로 이걸 누른 다음

VS Code의 'Install from VSIX 메뉴

오프라인에 저장된 폴더를 지정하면 설치가 진행됩니다.

이 정도면 될 것 같은데, 다른 것이 필요하다면 위 사이트에서 맨 위 메뉴에서 ‘Top Extensions’ 메뉴를 클릭한 다음 원하는 것을 다운로드 받아 설치하면 됩니다.

VSIX Top Extensions 메뉴

아래로 내려가면 Jupyter Notebook ‘Download VSIX‘ 버튼이 있습니다.

Jupyter noterbook VSIX 파일 다운로드 사이트

3. Python 관련 라이브러리 설치

많이 사용하는 파이썬 라이브러리로는 Pandas, Numpy, Openpyxl, Matplotlib, Seaborn 등등이 있습니다.

인터넷이 지원된다면

pip install pandas numpy openpyxl matplotlib seaborn

으로 한번에 설치할 수 있는데, 안된다면 매우 불편하지만 관련 whl(wheel) 파일을 다운로드 받아 설치해야 합니다.

하나만 해보면 나머지는 동일하게 하게 됩니다.

가. pansdas 라이브러리 파일 다운로드

폴더를 하나 만들어 거기에 관련된 whl 파일을 모두 넣은 다음 압축 파일로 만들어 넘기는 것이 좋습니다.

① 명령 프롬프트 창을 띄웁니다.

검색 창에 cmd라고 입력하면 명령 프롬프트가 표시되므로 클릭하면 됩니다.

명령 프롬프트 실행하기

② 폴더(디렉토리)를 만들고, 그 폴더로 이동하기

cd 명령을 이용해서 원하는 폴더로 이동한 다음

md wheel (wheel 폴더를 만듦)

cd wheel (wheel 폴더로 이동)

③ pip download pandas

pip download pandas 라고 입력하면 pandas만 다운로드 하는 것이 아니라 관련된 라이브러리를 함께 다운로드 받습니다.

맨 아래 줄에 “Successfully downloaded pandas numpy python-dateutil pytz six tzdata”라고 되어 있어 numpy도 포함되어 있습니다.

pandas 관련 wheel 파일을 다운로드 받기

이런 식으로 openpyxl 등 필요한 라이브러리를 모두 다운도드 받는데,

여러 개를 한꺼번에 다운로드 받으려면

pip download pandas openpyxl

이라고 하면 됩니다.

④ wheel 폴더 내의 파일들을 압축합니다.

pandas 관련 다운로드된 wheel 파일

나. 오프라인 PC로 옮긴 다음 설치하기

압축 파일을 오프라인 PC로 옮긴 다음

압축을 풀고 그 폴더에서

pip install –no-index –find-links=./ pandas openpyxl

식으로 관련 라이브러리명을 한꺼번에 지정해서 설치할 수 있고,

pandas 라이브러리만 설치한다고 하면

pip install –no-index –find-links=./ pandas

라고 하면 됩니다.

pandas 라이브러리 설치 화면

이미 설치되어 있기 때문에 Requirement already satisfied라고 나왔지만 설치되어 있지 않다면 성공적으로 인스톨됐다는 메시지가 나올 것입니다.

다. 주의할 점

python 버전이 맞지 않으면 아래와 같은 에러가 발생하므로

python 버전 불일치로 pip install 실패 화면

python -V 를 실행해서 파이썬 버전을 확인한 후

pip download pandas –only-binary=:all: –python-version 312 –platform win_amd64

라고 python 버전, 여기서는 3.12를 312로 지정한 후 whl 파일을 다운로드 받아야 합니다.

다른 언어와 비교한 Rust의 동시성(concurrency) 장,단점

Rust의 동시성(concurrency)은 안전성과 성능을 모두 고려한 설계로, 데이터 경쟁(data race)을 컴파일 타임에 방지하고, 성능 저하 없이 병렬 처리를 가능하게 합니다. C++, Java, Python, Go 등 타 언어와 비교해 장단점을 알아보겠습니다.

1. Rust의 동시성 개념

Rust는 동시성을 다음 세 가지 방식으로 지원합니다:

  1. 스레드 기반 동시성 (std::thread)
    • OS 스레드를 생성하여 병렬 작업 수행.
    • thread:spawn을 통해 새로운 스레드를 실행.
  2. 메시지 기반 통신 (std::sync::mpsc)
    • 채널을 통해 스레드 간 데이터 교환.
    • 데이터 소유권을 안전하게 이동.
  3. 비동기 프로그래밍 (async/await, tokio, async-std)
    • 효율적인 I/O 처리.
    • 싱글 스레드에서 수천 개의 작업을 동시에 처리할 수 있음.
    • Future를이용한 논블로킹 방식.

2. Rust 동시성의 핵심 특징

특징설명
데이터 레이스 방지컴파일 타임에 mut, &mut, Send, Sync 등을 통해 공유 자원에 대한 안전성 확보
제로 코스트 추상화고급 추상화를 사용해도 런타임 오버헤드 없음
fearless concurrency안전하게 동시성을 구현할 수 있어 “두려움 없는 동시성”이라고도 불림

3. 타 언어와의 비교

가. Rust vs C++

항목RustC++
안전성컴파일 타임 데이터 레이스 방지런타임에서 버그 발견 가능
메모리 모델소유권 시스템수동 메모리 관리
쓰레드 API안전하고 모던한 추상화복잡하고 안전하지 않은 경우 많음

🔹 Rust는 안전하고 버그 없는 병렬 처리를 제공
🔸 C++은 성능은 뛰어나지만 관리 책임이 개발자에게 있음 (예: 뮤텍스 실수 → 데이터 손상)

나. Rust vs Java

항목RustJava
런타임없음 (네이티브 실행)JVM 기반
동기화Mutex, RwLock, channel 등 명시적synchronized, volatile, ExecutorService 등
성능시스템 수준 고성능GC와 JVM 오버헤드 존재

🔹 Rust는 GC 없는 고성능 동시성
🔸 Java는 GC로 메모리 관리가 쉽지만 지연 가능성 존재

다. Rust vs Python

항목RustPython
성능매우 빠름느림 (인터프리터 기반)
GIL (Global Interpreter Lock)없음있음 (멀티 코어 병렬 처리 불가)
비동기 처리고성능 async/awaitasyncio로 가능하나 성능은 낮음

🔹 Rust는 진짜 병렬 처리 가능
🔸 Python은 GIL 때문에 CPU 병렬처리에 약함 (I/O 병렬만 현실적)

라. 요약

구분Rust의 장점Rust의 단점
성능네이티브 수준의 성능안전성을 위한 빌드 시간 증가
안전성데이터 레이스를 컴파일 타임에 방지초기 진입 장벽 (개념이 복잡함)
표현력async/await, channel, Mutex 등 현대적 추상화도구/라이브러리 생태계가 다른 언어보다 적은 편
병렬성GIL 없음, 진짜 병렬 처리 가능쓰레드 디버깅이 어려울 수 있음

4. Rust 동시성이 특히 유리한 분야

  • 고성능 웹 서버 (예: Actix, Axum)
  • 실시간 시스템 (예: 게임, IoT)
  • 병렬 데이터 처리 (예: 이미지/영상 처리)
  • 시스템 프로그래밍 (드라이버, 임베디드)

5. 1부터 100만까지 숫자의 합을 4개 스레드로 나눠 병렬 계산 비교

가. Rust 버전

use std::thread;

fn main() {
let data: Vec<u64> = (1..=1_000_000).collect();
let chunk_size = data.len() / 4;

let mut handles = vec![];

for i in 0..4 {
let chunk = data[i * chunk_size..(i + 1) * chunk_size].to_vec();
let handle = thread::spawn(move || chunk.iter().sum::<u64>());
handles.push(handle);
}

let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum();
println!("합계: {}", total);
}

설명:

use std::thread;
  • Rust의 표준 라이브러리에서 thread 모듈을 가져옵니다. 병렬 처리를 위해 사용됩니다.
let data: Vec<u64> = (1..=1_000_000).collect();
  • (1..=1_000_000)는 표현식이며 RangeInclusive 타입으로, 1부터 1_000_000까지 포함하는 이터레이터입니다.
  • .collect()는 이터레이터(iterator)를 모아서 컬렉션(예: Vec, HashMap, String)으로 변환하는 메서드입니다.
  • 명시적으로 Vec 타입을 선언했기 때문에, collect()는 모든 숫자를 벡터로 수집하게 됩니다.
let chunk_size = data.len() / 4;
  • 데이터를 4개의 스레드로 나눌 것이기 때문에, 각 스레드가 처리할 데이터의 크기를 계산합니다.
  • chunk_size = 250_000
let mut handles = vec![];
  • 스레드 핸들(JoinHandle)들을 저장할 벡터.
  • 각 스레드는 나중에 .join()으로 결과를 수집할 수 있습니다.
for i in 0..4 {
let chunk = data[i * chunk_size..(i + 1) * chunk_size].to_vec();
let handle = thread::spawn(move || chunk.iter().sum::<u64>());
handles.push(handle);
}
  • 루프를 4번 돌며 벡터를 4등분합니다.
let chunk = data[i * chunk_size..(i + 1) * chunk_size].to_vec();
  • data를 4등분하여 각 스레드에 넘길 복사본을 만든 후 chunk에 할당합니다.
  • i가 0~3까지 반복되므로:
    • i = 0 일 때는 data[0..250000]
    • i = 1 일 때는 data[250000..500000]
    • i = 2 일 때는 data[500000..750000]
    • i = 3 일 때는 data[750000..1000000]
  • 이렇게 전체 데이터를 4개의 슬라이스(slice)로 나눕니다. 하지만 슬라이스는 참조(&)이며, 여러 스레드가 같은 데이터를 공유할 때, 데이터 경합(data race)을 막기 위해 컴파일러가 참조의 안전성을 보장해야 하므로, .to_vec()을 사용하여 슬라이스의 복사본을 만들어 소유권을 가지는 새 벡터로 만듭니다. 이제 이 벡터는 독립적 소유권을 가지므로, move를 통해 클로저에 안전하게 넘길 수 있습니다
let handle = thread::spawn(move || chunk.iter().sum::<u64>());
  • thread::spawn(…) → 새로운 스레드(thread)를 만들어서 주어진 작업을 실행합니다.
  • move || … → 클로저(익명 함수)에서 외부 변수인 chunk의 소유권을 이동시켜 사용합니다.
  • chunk.iter().sum::() → chunk의 모든 원소를 합산하여 u64값을 반환합니다.
  • 반환된 handle은 JoinHandle 타입이고, 이걸 handles 벡터에 저장해 나중에 결과를 수집합니다.
handles.push(handle);
  • thread::spawn(…)의 결과인 JoinHandle을 handles 벡터에 저장합니다.
  • 이 handles는 모든 스레드 작업이 끝난 뒤 결과를 수집하는 데 사용됩니다.
let total: u64 = handles.into_iter().map(|h| h.join().unwrap()).sum();
  • 스레드에서 계산한 4개의 부분합을 모아서 전체 합을 계산한다.
  • handles.into_iter() .into_iter()는 handles 벡터의 소유권을 consuming iterator로 가져옵니다(copy가 아닌 move). 즉, 이후 handles는 더 이상 사용할 수 없습니다.
  • .map(|h| h.join().unwrap())의각 h는 JoinHandle 이고, h.join()은 이 스레드가 끝날 때까지 기다리고, .unwrap()으로 에러 무시하고 강제 추출합니다. .map(…) 부분은 4개의 스레드를 기다리며 각각의 계산된 합을 모아 [u64; 4] 형태로 만듭니다
  • .sum()은 [u64; 4]을 전부 더해서 최종 합계를 구해서, total에 할당합니다.
println!("합계: {}", total);
  • “합계: 500000500000″을 출력합니다.
    500000500000은 1000000 * 1000001 / 2 = 500000500000입니다.

나. Python (threading 사용, CPU 병렬 처리 실패 예)

import threading

data = list(range(1, 1_000_001))
results = [0] * 4

def worker(idx, chunk):
results[idx] = sum(chunk)

threads = []
chunk_size = len(data) // 4

for i in range(4):
t = threading.Thread(target=worker, args=(i, data[i*chunk_size:(i+1)*chunk_size]))
threads.append(t)
t.start()

for t in threads:
t.join()

print("합계:", sum(results))

🔸 설명:

  • Python은 GIL(Global Interpreter Lock) 때문에 진짜 병렬 아님
  • threading은 CPU 병렬 처리 불가 → 오히려 느림
  • multiprocessing을 쓰면 병렬 가능하지만 복잡도 증가

다. Java 버전

import java.util.concurrent.*;

public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newFixedThreadPool(4);
int[] data = new int[1_000_000];
for (int i = 0; i < data.length; i++) data[i] = i + 1;

Future<Long>[] results = new Future[4];
int chunkSize = data.length / 4;

for (int i = 0; i < 4; i++) {
final int start = i * chunkSize;
final int end = (i + 1) * chunkSize;
results[i] = executor.submit(() -> {
long sum = 0;
for (int j = start; j < end; j++) sum += data[j];
return sum;
});
}

long total = 0;
for (Future<Long> result : results) {
total += result.get();
}

executor.shutdown();
System.out.println("합계: " + total);
}
}

🔸 설명:

  • ExecutorService를 통해 병렬 처리
  • 비교적 안전하지만, Future.get()이 blocking이며 예외 처리 필요
  • GC가 있어 런타임 예측 어려움

라. C++ 버전 (C++11 이상)

#include <iostream>
#include <vector>
#include <thread>
#include <numeric>

int main() {
std::vector<uint64_t> data(1'000'000);
std::iota(data.begin(), data.end(), 1);

uint64_t partial_sums[4] = {};
std::vector<std::thread> threads;

size_t chunk_size = data.size() / 4;

for (int i = 0; i < 4; ++i) {
threads.emplace_back([i, chunk_size, &data, &partial_sums]() {
partial_sums[i] = std::accumulate(
data.begin() + i * chunk_size,
data.begin() + (i + 1) * chunk_size,
0ULL
);
});
}

for (auto& t : threads) t.join();

uint64_t total = 0;
for (auto s : partial_sums) total += s;

std::cout << "합계: " << total << std::endl;
}

🔸 설명:

  • 고성능이지만, &data, &partial_sums는 데이터 경쟁 가능성 존재
  • 공유 자원 동기화에 실패하면 잘못된 결과 나올 수 있음
  • 동기화 도구 사용 시 성능 저하 우려

마. 언어별 동시성 비교

언어병렬 처리 성능안전성코드 복잡도주의사항
Rust매우 뛰어남컴파일 타임 안전 보장다소 복잡소유권, 라이프타임 이해 필요
Python낮음 (GIL)안전하나 느림간단multiprocessing 사용 시 복잡
Java중간런타임 에러 가능보통예외 처리, GC
C++고성능데이터 레이스 가능복잡직접 동기화 필요

6. Rust vs Go 동시성 비교 요약

항목RustGo
동시성 모델명시적 스레드 + 채널 + async/await경량 고루틴(goroutine) + 채널(channel)
메모리 관리수동 + 소유권 시스템 (GC 없음)GC 있음 (자동 메모리 관리)
안전성컴파일 타임에 데이터 경쟁 차단런타임에 데이터 레이스 가능 (race detector 필요)
런타임없음 (zero-cost abstraction)있음 (스케줄러 + GC)
학습 곡선가파름 (소유권/라이프타임 개념 필요)비교적 완만
성능매우 뛰어남 (GC 없음)빠르지만 GC 오버헤드 존재

7. 10개의 작업을 동시 실행 비교

가. Rust:

use std::thread;

fn main() {
let mut handles = vec![];

for i in 0..10 {
let handle = thread::spawn(move || {
println!("Rust 스레드 {} 실행", i);
});
handles.push(handle);
}

for handle in handles {
handle.join().unwrap();
}
}

✅ 특징:

  • std::thread::spawn으로 OS 스레드 생성
  • 스레드 수 제한 없음 (하지만 무거움)
  • 안전하게 소유권 이동 (move) → 데이터 경쟁 없음

나. Go: 고루틴 10개 실행

package main

import (
"fmt"
"time"
)

func main() {
for i := 0; i < 10; i++ {
go func(i int) {
fmt.Printf("Go 고루틴 %d 실행\n", i)
}(i)
}

time.Sleep(time.Second) // 고루틴이 끝날 시간 대기
}

✅ 특징:

  • go 키워드 하나로 병렬 실행
  • 고루틴은 스레드보다 훨씬 가볍고 수천 개 생성 가능
  • 단, 공유 자원 접근 시 데이터 레이스 가능 (예: i 변수 캡처 문제 발생 가능)

다. 동시성 핵심 차이

항목RustGo
실행 단위OS 스레드 또는 async task고루틴 (경량 스레드)
병렬 처리 수단스레드, 채널, async/await고루틴, 채널
데이터 보호컴파일 타임 소유권 체크뮤텍스, 채널, race detector
동시성 철학“Fearless Concurrency” (두려움 없는 동시성)“Do not communicate by sharing memory…”
GC없음 (직접 메모리 관리)있음 (자동 정리되지만 성능 오버헤드 발생 가능)

라. 성능과 안전성 비교

항목RustGo
성능GC가 없어서 시스템 자원 최대 활용GC와 스케줄러의 오버헤드 존재
안전성데이터 경쟁을 컴파일 타임에 방지기본적으로 가능함 (race detector로 검사해야 함)
스케일링수천 개의 작업 처리 시 async 필요수천 개 고루틴도 가볍게 처리 가능
디버깅 난이도복잡 (라이프타임, borrow checker 등)비교적 단순

마. 상황별 언어 선택

상황Rust 추천Go 추천
고성능 시스템 (e.g. 게임, 실시간 처리, WebAssembly)X
빠른 개발, 유지보수 쉬운 서버 (e.g. 웹 API, 클라우드 백엔드)가능하지만 무겁고 복잡매우 적합
메모리 제어 필요 (e.g. 임베디드, 드라이버)X
초고성능 네트워크 서버 (e.g. Actix, Tokio 기반)GC로 한계 가능
간단한 병렬 작업, CLI 툴한계

바. 결론

항목RustGo
성능최상급좋음 (하지만 GC 존재)
안전성컴파일 타임 보장런타임 race 가능
개발 속도어렵고 장벽 높음빠르고 쉬움
확장성 (스케일)async 사용 시 매우 뛰어남고루틴 덕분에 뛰어남
유지보수성복잡간단하고 명확

사. 요약

  • Rust: 동시성을 정밀하게 제어해야 하거나, 성능과 안전이 최우선인 경우 유리
  • Go: 빠르게 개발하고, 다수의 작업을 단순하게 병렬 처리할 때 탁월