Something Is Weird About Rust's Threading and Concurrency | Rust Multi-Threading Tutorial

preview_player
Показать описание
Concurrency and synchronization is an extremely important topic in computer programming. How can I use multi threaded code to execute computationally intensive algorithms? While it's easy to do in most languages, designing a system that allows systems to access the same data at the same time is dangerous and easier said than done.

In this video I teach Multi Threaded Rust Programming, and use a more complicated example to describe how to provide multiple threads access to the same data.

Рекомендации по теме
Комментарии
Автор

The Mutex contains the data (and therefore owns the data), but who owns the Mutex when we are passing it into threads? Without answering this question Rust cannot automatically free its allocation correctly. Therefore we need Rc as a way to implement shared ownership; also we need the reference counting to be atomic to be thread-safe, ergo Arc. So in the end, it's Arc<Mutex<Struct>>. All pieces come nicely together and it all makes sense. It might seem complicated at first glance, but it's correct. This is what I love about Rust.

scheimong
Автор

Also FYI, for most primitive types, Rust has atomic primitives and they can be much faster. Instead of `Arc<Mutex<usize>>`, you can use `Arc<AtomicUsize>`.

johnstanford
Автор

I've been doing rust for 5 years and specialized in concurrency. The coolest things I've found are atomics, they unlock a whole world of efficient parallel computing. Feel free to reach out if you want to talk about concurrency stuff

Buzzec
Автор

You can also pass the mutex as a reference, but one limitation of thread::spawn is that it requires the 'static lifetime, which you can't achieve without leaking memory in your example. Thankfully there are libraries that help bridge that gap, like scoped_threadpool

shadamethyst
Автор

Perfect timing. Having a lecture about concurrency for the last few weeks and wanted to know how it works in Rust :)

SandwichMitGurke
Автор

great video! explained it well and in an interesting way. seemed a bit too basic to me though, i feel like anyone with basic knowledge of either rust or concurrency would know all of this. maybe do some videos on more complex concurrency in rust? i'd love to see that!

madscientist
Автор

Although this is complicated, it never feels like it's lazily developed. Someone put real thought into this and ensured it stays as simple as it can, while adhering to the fundamental memory safety of Rust.

For those who didn't get it, let me explain it this way:

You want a value to be incremented by multiple threads, but for that you need to define it in the scope of the main() function, just outside the for loop, but if you do that what the thread code gets is a copy of this value which it increments, which won't be reflected in the original.

So you gotta create a mutually exclusive reference to the value, which can be passed by value and dereferenced to get the value and increment it in such a way that only one thread is dereferencing at a time, which is where Mutex comes into the picture.

But now, the variable holding the Mutex cannot be "moved" into the thread now, so what I would generally do is a copy reference. But if I copy that Mutex, how would other threads get the same Mutex? They'll get its copy.

So now I need to pass a reference to THAT Mutex, but this reference is being made across multiple threads, which means Rust needs a way to know when all threads have called whatever references they want, and it can safely discard the original reference, so an atomic reference is used which is Arc.

I know my explanation is a bit lousy, but I'd love some feedback on it, because I'm also an absolute beginner in Rust

k_gold
Автор

Just use scoped threads and you will can borrow the thread vector.

jcbritobr
Автор

I'm just playing with Rust, but in mt early attempts to use threads, I found that declaring the mutex as static also works, without using Arc. After all, you do not need to mutate the mutex, only its contents.

FrancescoBochicchio-lydu
Автор

You might want to use a scoped thread to avoid Arc to achieve less allocation.

ickunph
Автор

Doesn't need to return a value explicitly. Like in this case, where `main` function returns `unit` type (aka `void`).
If you return a value don't need to write the code like this:
`return 5;`
Need to write this code:
`5`

jerrody
Автор

Thank you, now my headache can go away temporarily!

Echo_of_Abyss
Автор

i believe you could've covered what it means to "join a thread"

TiroM
Автор

Closures and their data capturing (move) forced me to learn about refcells, rcs and arcs. I almost formed a habit of moving everything from the main scope into a refcelled rc just to be safe for the future. 😅

misobarisic
Автор

Been doing this for 10 years in C++ Builder

colinmaharaj
Автор

Nice work. In idiomatic Rust you would more likely see something like

let handles: Vec<_> = (0..10).map( |i| ... ).collect();

but I'm guessing you were concerned that your audience might not be familiar with map and collect.

ClearerThanMud
Автор

I like the video format a lot, byte-sized concepts of rust. yet i would prefer there to be less stock footage / less switching between them. i find this to be mentally straining and distracting

TiroM
Автор

The following might seem like nitpicking but there is a critical point at stake here…

You are saying “concurrency” here but because you are talking about mutexes and sync issues associated with multiple threads, I think it would be much clearer to say “parallelism”. A program may utilise concurrency without creating multiple threads, i.e. without any parallelism. In other words, while parallelism implies that there is concurrency, the reverse is not true. And crucially from the perspective of what you’ve presented here, concurrency without parallelism has the major benefit of supporting lock-free algorithms and data structures. Mutexes or other sync mechanisms are only necessary in the context of concurrency achieved through parallelism, not concurrency generally.

Speaking of other sync mechanisms, a smaller point I wanted to mention is that if the requirement is just to sync access to a counter, it would be very inefficient in C/C++ to achieve this using a blocking mutex. It would be better to use an interlocked memory access API which provides an ability to atomically increment a counter in a thread-safe way using special CPU instructions. All of the major instruction set architectures in use today support interlocked memory access. The C++ Standard Library exposes the OS-specific interlocked API through the various templates and specialisations in the <atomic> header. I’m sure Rust has an equivalent.

Edit: I’ve just seen that another commenter has mentioned the Rust equivalent to <atomic>.

Baile_an_Locha
Автор

Great video! But would you mind removing the "return;" at the end of main? It is bad programming style and opposed to the functional base principle of "every block has a value", which rust adheres to. (opposed to blocks 'returning' a value)

m.sierra
Автор

Is this primarily going to be a Rust channel now?

NewEnglandMovies