1

I implemented a simple game of life and I want to try multithreading for the first time.

I use a 1D representation of the gamefield according to the formula index = y * height + x. Each cell is stored in a HashSet. A cell is alive if its contained in the set.

The relevant part of the code regarding the multithreading ([playground][1]):

extern crate crossbeam_utils; // 0.8.1
use std::collections::HashSet;
use std::sync::Arc;
use crossbeam_utils::thread;

pub struct GameOfLife {
    width: u32,
    height: u32,
    live_cells: HashSet<u32>,
}

impl GameOfLife {
    pub fn new(width: u32, height: u32) -> Self {
        GameOfLife {
            width,
            height,
            live_cells: HashSet::with_capacity((width * height / 2) as usize),
        }
    }

    fn live_neighbours(&self, pos: u32) -> usize {
        let indicies: [i64; 8] = [
            pos as i64 - self.width as i64 - 1,
            pos as i64 - self.width as i64,
            pos as i64 - self.width as i64 + 1,
            pos as i64 - 1,
            pos as i64 + 1,
            pos as i64 + self.width as i64 - 1,
            pos as i64 + self.width as i64,
            pos as i64 + self.width as i64 + 1,
        ];

        indicies
            .iter()
            .filter(|&&i| {
                i >= 0
                    && i < (self.width * self.height) as i64
                    && self.live_cells.contains(&(i as u32))
            })
            .count()
    }

    fn next_gen(&self, start: u32, stop: u32) -> HashSet<u32> {
        (start..stop)
            .into_iter()
            .filter(
                |&i| match (self.live_neighbours(i), self.live_cells.contains(&i)) {
                    (2, true) | (3, _) => true,
                    _ => false,
                },
            )
            .collect::<HashSet<u32>>()
    }

    pub fn next_generation(&mut self) {
        let size = self.width * self.height;
        let half = (size / 2) as u32;
        let mut new_cells = HashSet::<u32>::with_capacity(half as usize);
        {
            let h1 = thread::scope(|s| s.spawn(|_| self.next_gen(0, half))).unwrap();
            let h2 = thread::scope(|s| s.spawn(|_| self.next_gen(half, size))).unwrap();

            new_cells.extend(&h1.join().unwrap());
            new_cells.extend(&h2.join().unwrap());
        }
        self.live_cells = new_cells;
    }

}

fn main() {
    let mut gol = GameOfLife::new(170, 40);
    //seed random values ...
    
    for _i in 0..1000000 {
        gol.next_generation();
    }
}

I am using the crate crossbeam_util="0.8". I am now getting this error:

error: lifetime may not live long enough
  --> src/main.rs:60:40
   |
60 |             let h1 = thread::scope(|s| s.spawn(|_| self.next_gen(0, half))).unwrap();
   |                                     -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
   |                                     ||
   |                                     |return type of closure is ScopedJoinHandle<'2, HashSet<u32>>
   |                                     has type `&'1 Scope<'_>`

Updated playground link: playground

Curunir
  • 1,186
  • 2
  • 13
  • 30
  • @Curunir `extern crate` Hasn’t been needed for the last three years, so that’s not really a valid distinction. – Shepmaster Feb 08 '21 at 19:59
  • @kmdreko not really, how do I best show the issues I have with it? Edit original post? – Curunir Feb 08 '21 at 20:19
  • Yup. Ideally, link to the proposed duplicate and explain how yours differs – Shepmaster Feb 08 '21 at 20:31
  • 1
    [Here's the answer to the other question applied to your case.](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f9d33309c8478876b1d72079451852f0) Note that with scoped threads you do not have to wrap everything in `Arc` and `clone` it, because shared `&` references are `Copy` anyway. I can't think of any good reason to put a `&` reference directly inside an `Arc`. – trent Feb 08 '21 at 22:05
  • If that doesn't work for you, please do edit the question to demonstrate how your question is different; we can easily reopen this one. – trent Feb 08 '21 at 22:08
  • @Shepmaster I added the changes according to suggested duplicate, error output and link to playground – Curunir Feb 09 '21 at 07:04
  • The "scope" in a scoped thread has to outlive all the threads it spawns; that's the point of it. The playground I linked earlier should satisfy your requirements. – trent Feb 10 '21 at 00:46

0 Answers0