Marcin Jahn | Dev Notebook
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • Home
  • Programming
  • Technologies
  • Projects
  • About
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects
  • An icon of the Core section Core
    • Programs Execution
    • Stack and Heap
    • Asynchronous Programming
      • Overview
      • Event Queues
      • Fibers
      • Stackless Coroutines
  • An icon of the .NET section .NET
    • HTTPClient
    • Async
      • How Async Works
      • TAP Tips
    • Equality
    • Comparisons
    • Enumerables
    • Unit Tests
    • Generic Host
    • Logging
    • Configuration
    • Records
    • Nullability
    • Garbage Collector
    • IL and Allocations
    • gRPC
    • Source Generators
    • Platform Invoke
    • ASP.NET Core
      • Overview
      • Middleware
      • Razor Pages
      • Routing in Razor Pages
      • Web APIs
      • Filters
      • Identity
      • Validation
      • Tips
    • Entity Framework Core
      • Overview
      • Testing
      • Tips
  • An icon of the Angular section Angular
    • Overview
    • Components
    • Directives
    • Services and DI
    • Routing
    • Observables (RxJS)
    • Forms
    • Pipes
    • HTTP
    • Modules
    • NgRx
    • Angular Universal
    • Tips
    • Standalone Components
  • An icon of the JavaScript section JavaScript
    • OOP
    • JavaScript - The Weird Parts
    • JS Functions
    • ES Modules
    • Node.js
    • Axios Tips
    • TypeScript
      • TypeScript Environment Setup
      • TypeScript Tips
    • React
      • React Routing
      • MobX
    • Advanced Vue.js Features
  • An icon of the Rust section Rust
    • Overview
    • Cargo
    • Basics
    • Ownership
    • Structures
    • Enums
    • Organization
    • Collections
    • Error Handling
    • Generics
    • Traits
    • Lifetimes
    • Closures
    • Raw Pointers
    • Smart Pointers
    • Concurrency
    • Testing
    • Tips
  • An icon of the C/C++ section C/C++
    • Compilation
    • Structures
    • OOP in C
    • Pointers
    • Strings
    • Dynamic Memory
    • argc and argv Visualization
  • An icon of the GTK section GTK
    • Overview
    • GObject
    • GJS
  • An icon of the CSS section CSS
    • Responsive Design
    • CSS Tips
    • CSS Pixel
  • An icon of the Unity section Unity
    • Unity
  • An icon of the Functional Programming section Functional Programming
    • Fundamentals of Functional Programming
    • .NET Functional Features
    • Signatures
    • Function Composition
    • Error Handling
    • Partial Application
    • Modularity
    • Category Theory
      • Overview
      • Monoid
      • Other Magmas
      • Functors
  • An icon of the Algorithms section Algorithms
    • Big O Notation
    • Array
    • Linked List
    • Queue
    • Hash Table and Set
    • Tree
    • Sorting
    • Searching
  • An icon of the Architecture section Architecture
    • What is architecture?
    • Domain-Driven Design
    • ASP.NET Core Projects

Concurrency

Just like with memory, Rust is quite good at finding out potential concurrency problems during compilation.

Multithreading

Rust implements 1:1 threads, so when we create a thread in Rust, an actual thread is also created by the OS. There are crates that implement M

threading.

Creating a New Thread

thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});

Threads run closures. If the main() function finishes before threads finish execution, they will be terminated anyway.

Awaiting Threads

A JoinHandle may be used to wait for a thread to finish its execution. Its is returned by thread::spawn().

fn main() {
let handle = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from the spawned thread!", i);
thread::sleep(Duration::from_millis(1));
}
});
handle.join().unwrap();
}

The app will finish only when the thread is done.

Environment Ownership

Sometimes we might want one thread to pass ownership of some value to another thread. We can do that with the move keyword placed before the closure.

fn main() {
let v = vec![1, 2, 3];
let handle = thread::spawn(move || {
println!("Here's a vector: {:?}", v);
});
handle.join().unwrap();
}

Messaging

Threads can communicate via channels where a transmitter sends messages and receiver receives them.

use std::sync::mpsc;
use std::thread;
use std::time::Duration;
let (tx, rx) = mpsc::channel(); // create transmitter and receiver
thread::spawn(move || {
let vals = vec![
String::from("more"),
String::from("messages"),
String::from("for"),
String::from("you"),
];
for val in vals {
tx.send(val).unwrap(); // send a message
thread::sleep(Duration::from_secs(1));
}
});
// receive messages (treats rx as an iterator)
for received in rx {
println!("Got: {}", received);
}

mpsc

mpsc module stands for Multiple Producers, Single Consumer. We could clone tx and have another transmitter connected to rx.

Multiple receivers are not allowed in this model.

The send() method takes ownership of the value being sent and transfers ownership to the receiving thread. d

Receiving

The receiver can use the following way to receive messages:

  • iterator (as shown) - blocking, receives all messages.
  • recv() method - blocking, receives 1 message.
  • try_recv() - non-blocking, receives 0 or 1 message.

Shared Memory

Mutex

Data can be guarded by a mutex, which allows access to that data to only one thread at a time.

use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
let mut num = m.lock().unwrap();
*num = 6;
}
println!("m = {:?}", m);
}

To get access to data protected by a Mutex, we have to call lock() on it. This is a blocking call, execution will continue as soon as either:

  • lock is free
  • another thread that had the lock panics

Smart Pointer

lock() returns a MutexGuard smart pointer.

The lock is released as soon as MutexGuard goes out of scope.

Interior Mutability

Mutex<T> is a bit similar to RefCell<T>. Even though the Mutex instance is defined as immutable, we can change the value inside of it.

Deadlock

Threads using Mutex can suffer from deadlocks!

Arc

We can’t use Rc<T> in multithreaded contexts. Instead, we can use Arc<T>, which is thread-safe (and has some performance overhead due to that). We can use Arc to share values between many threads. The example below shares a Mutex:

use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter); // like Rc, Arc can be cloned to copy ownership to the value
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1; // we increment in one thread at a time thanks to Mutex
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// counter has value 10
}

Traits

There are a few traits that are important for concurrency.

Send

The Send marker trait indicates that ownership of values of the type implementing Send can be transferred between threads. Most types implement it with some exceptions (like Rc, which is not thread-safe). Any type that contains only Send-marked fields is automatically marked as Send as well.

Sync

The Sync marker trait indicates that it is safe for the type implementing Sync to be referenced from multiple threads. Similar to Send, types composed entirely of types that are Sync are also Sync.

Manually implementing Send and Sync is unsafe.

←  Smart Pointers
Testing  →
© 2023 Marcin Jahn | Dev Notebook | All Rights Reserved. | Built with Astro.