ABOUT ME

-

Total
-
  • Rust: Go와 비슷하게 멀티쓰레딩 짜기
    컴퓨터/Rust 2022. 8. 29. 14:37
    728x90
    반응형
    my_map := make(map[string]string)
    var wg sync.WaitGroup
    wg.Add(len(my_map ))
    
    for key := range my_map {
        go func(key string) {
            defer wg.Done()
            stdout, _ := exec.Command(key, "some command").Output()
    
            lock.Lock()
            defer lock.Unlock()
            my_map[key] = "updating map value while iterating"  // eg stdout
        }(key)
    }

    어느 날 위와 같이 Go언어로 작성한 것을 Rust로 옮기려다가 배운 것이다.

    위 Go언어으로 짠 걸 보면 우선 Map을 만들고 key마다 돌면서 thread를 spawn 하고 각 key를 업데이트한다.

    Go에서는 쉽게 sync.WaitGroup과  lock을 이용해서 멀티쓰레드를 할 수 있었는데

     

    Rust에서 짜려니 개념을 잘 몰랐다.

    let mut my_map = HashMap::new();
    
    ...
    
    for (key, value) in my_map.iter_mut() {
        // Should update map value, tried with crossbeam_utils:thread;
    
        thread::scope(|s| {
            s.spawn(|_| {
                let cmd = format!("some command {key}");  // will borrow key
                let cmd: Vec<&str> = cmd.as_str().split(" ").map(|s| s).collect();
    
                let proc = Popen::create(
                    &cmd,
                    PopenConfig {
                        stdout: Redirection::Pipe,
                        stdin: Redirection::None,
                        stderr: Redirection::None,
                        ..Default::default()
                    },
                );
    
                let mut proc = proc.expect("Failed to create process.");
    
                let (out, _) = proc.communicate(None).unwrap();
                let stdout = out.unwrap();
    
                *value = "I will update value with something new";
            });
        })
        .unwrap();
    }

    처음에 위처럼 짰다가 각 key 마다 thread를 spawn은 하는데 다음 key로 iterate 할 때까지 thread가 join 된다는 것이었다.

    또 Rust는 borrowing 기능도 있어 위처럼 하면 mutable borrowing 에러가 난다.

    원하는 방식은 Go처럼 그냥 thread를 다 spawn 해놓고 map for문 끝나면 거의 동시에 다 같이 value를 업데이트하는 것이다.

    Rust는 heavy OS 쓰레드 방식 (stack 갖고 있음), Go는 green 쓰레딩 모델이라고 한다.

     

    수정한 방식은 애초에 thread::scope를 이용해서 새로운 영역을 만들고 안에서 쓰레드를 spawn하면

    non 'static object도 borrow 할 수가 있다.

    그리고 영역이 끝날 때는 모든 scoped thread들이 join 하기 때문에 이후에도 계속 변수를 사용할 수가 있다.

     

    #![allow(unused)]
    fn main() {
    use std::thread;
    
    let mut a = vec![1, 2, 3];
    let mut x = 0;
    
    thread::scope(|s| {
        s.spawn(|| {
            println!("hello from the first scoped thread");
            // We can borrow `a` here.
            dbg!(&a);
        });
        s.spawn(|| {
            println!("hello from the second scoped thread");
            // We can even mutably borrow `x` here,
            // because no other threads are using it.
            x += a[0] + a[2];
        });
        println!("hello from the main thread");
    });
    
    // After the scope, we can modify and access our variables again:
    a.push(4);
    assert_eq!(x, a.len());
    }
    728x90

    댓글