Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

📝 Knowledge Test

/
Knowledge Test Score Board
· 0%

Work through the questions below — your score updates as you go.

Keep going · grade bands mapped to DCU's honours scale

target20:00

Only your first attempt at each question counts toward this score. Reload the page to reset.

This section contains 20 interactive knowledge checks designed to test your understanding of the materials covered in Chapter 10. The questions cover OS threads, message passing via channels, shared state using Mutex and Arc, atomic operations, and the marker traits that guarantee thread safety.

Please remember that many of the quizzes have multiple correct answers, and you must select all applicable options to succeed. I have carefully balanced the lengths of the answers, so take your time to evaluate each option thoroughly.

Good luck!

Derek.


Q1
Concept Match

Match the Concurrency Fundamentals

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Concurrency
drag a definition here…
Parallelism
drag a definition here…
Process
drag a definition here…
Thread
drag a definition here…

Definition Pool

Physical simultaneity where multiple computations execute at the exact same instant.
A structural property where multiple tasks make progress in overlapping time periods.
An isolated execution environment with private memory space and resources.
A lightweight unit of execution that lives inside and shares memory with a process.
Q2
Quiz
Select 0/2

Which of the following statements accurately describe the behaviour of thread::spawn() in Rust?

It creates a new OS thread and blocks the main thread until the new thread starts running.
It automatically ensures that all spawned threads are joined before the main function returns.
It requires a closure that must satisfy the 'static lifetime bound if it does not use scoped threads.
It returns a JoinHandle, which can be used to wait for the thread's completion and retrieve its result.
Q3
Code Cloze
Rust

Spawning and Joining Threads

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::thread;
2
3fn main() {
4 // Spawn a thread that returns a value
5 let handle = thread::·····(|| {
6 42
7 });
8
9 // Wait for the thread to finish and retrieve the result
10 let result = handle.·····().unwrap();
11 println!("Thread returned: {}", result);
12}

Available Snippets

join
spawn
wait
run
await
create
Q4
Quiz
Select 0/1

Why is the 'move' keyword frequently required when spawning a new thread?

To satisfy the thread-safety requirements of the operating system's kernel-level scheduler.
To force the closure to take ownership of captured variables, ensuring they live long enough.
To prevent the main thread from accessing any variables until the spawned thread finishes.
To tell the compiler to optimise the closure body for faster concurrent execution.
Q5
Concept Match

Match the Channel Components

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

mpsc
drag a definition here…
Sender
drag a definition here…
Receiver
drag a definition here…
try_recv
drag a definition here…

Definition Pool

The transmitting end of a channel; it can be cloned to allow multiple producer threads.
Stands for 'Multiple Producer, Single Consumer', the standard channel type in Rust.
The receiving end of a channel; it cannot be cloned, ensuring a single consumer.
A non-blocking receive method that returns an error immediately if no message is waiting.
Q6
Code Output
Rust

Predict the output: Channel Communication

Read the code below, then choose the terminal output it produces and click Submit.

1use std::sync::mpsc;
2use std::thread;
3
4fn main() {
5 let (tx, rx) = mpsc::channel();
6
7 thread::spawn(move || {
8 tx.send(10).unwrap();
9 tx.send(20).unwrap();
10 });
11
12 let val1 = rx.recv().unwrap();
13 let val2 = rx.recv().unwrap();
14 println!("{} {}", val1, val2);
15}
stdout
10 20
stdout
20 10
stdout
10 10
stdout
20 20
Q7
Quiz
Select 0/1

When does a loop using a Receiver as an iterator (e.g., for msg in rx { ... }) terminate?

When all corresponding Sender handles have been dropped and the channel buffer is empty.
Exactly 60 seconds after the last message was successfully transmitted through the pipe.
As soon as the buffer is empty, regardless of whether senders are still active.
Only when the developer explicitly calls an rx.close() method from the consumer thread.
Q8
Quiz
Select 0/1

In the context of Assignment 2 and responsive GUIs, why is try_recv() preferred over recv() inside the update() loop?

Because recv() requires a mutable reference to the context, which is not available in egui.
Because recv() is a blocking call that would freeze the GUI until a new message arrives.
Because try_recv() automatically handles the synchronisation of background hardware threads.
Because try_recv() is significantly faster at processing complex data structures like JSON.
Q9
Concept Match

Match the Synchronisation Primitives

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Mutex<T>
drag a definition here…
RwLock<T>
drag a definition here…
Arc<T>
drag a definition here…
MutexGuard
drag a definition here…

Definition Pool

Allows multiple concurrent readers or exactly one exclusive writer; ideal for read-heavy data.
Enforces mutual exclusion, allowing only one thread at a time to access the protected data.
An Atomic Reference Counted pointer that enables multiple threads to share ownership of data.
A smart pointer that provides data access and automatically releases the lock when dropped.
Q10
Code Cloze
Rust

Sharing a Mutex with Arc

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::sync::{Arc, Mutex};
2use std::thread;
3
4fn main() {
5 // Create a shared counter
6 let counter = Arc::new(Mutex::new(0));
7 let mut handles = vec![];
8
9 for _ in 0..10 {
10 // Clone the Arc to share ownership
11 let counter_clone = Arc::·····(&counter);
12 let handle = thread::spawn(move || {
13 // Acquire the lock
14 let mut num = counter_clone.·····().unwrap();
15 *num += 1;
16 });
17 handles.push(handle);
18 }
19}

Available Snippets

acquire
new
lock
read
clone
copy
Q11
Quiz
Select 0/1

What is 'Mutex Poisoning' in Rust?

A hardware failure where the CPU fails to correctly execute an atomic compare-and-swap instruction.
An optimisation where the compiler permanently locks a mutex to prevent any further modifications.
A state where a thread panics while holding a lock, indicating the protected data may be inconsistent.
A security vulnerability where an external process injects malicious code into a shared memory region.
Q12
Quiz
Select 0/2

Why is Arc<T> used instead of Rc<T> for sharing data between threads?

Because Arc allows the data to be stored in the program's read-only binary section.
Because Rc is restricted to sharing only primitive types like integers and booleans.
Because Arc uses atomic operations to update the reference count, ensuring it is thread-safe.
Because Rc uses non-atomic counters which could lead to data races on the reference count itself.
Q13
Concept Match

Match the Advanced Sync Concepts

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

AtomicBool
drag a definition here…
Condvar
drag a definition here…
Deadlock
drag a definition here…
SeqCst
drag a definition here…

Definition Pool

A situation where two threads are each waiting for a lock held by the other, stalling indefinitely.
Allows a thread to sleep efficiently until another thread signals that a condition has changed.
The strongest memory ordering, ensuring a consistent global order of atomic operations.
A thread-safe boolean that can be loaded and stored without the overhead of a mutex lock.
Q14
Code Cloze
Rust

Using an Atomic Stop Flag

Drag snippets from the pool into the blanks so the program produces the output shown, then click Submit.

1use std::sync::atomic::{AtomicBool, Ordering};
2
3fn main() {
4 let stop_flag = AtomicBool::new(false);
5
6 // Set the flag atomically
7 stop_flag.·····(true, Ordering::SeqCst);
8
9 // Read the flag atomically
10 if stop_flag.·····(Ordering::SeqCst) {
11 println!("Stopping...");
12 }
13}

Available Snippets

store
get
read
load
write
set
Q15
Quiz
Select 0/1

In a read-heavy workload where 100 threads read a value and only 1 thread writes it, why is RwLock<T> superior to Mutex<T>?

Because RwLock uses a more advanced hardware-level cache that is only available on multi-core ARM processors.
Because Mutex requires a separate memory allocation for every reader thread that attempts to acquire the lock.
Because RwLock allows all 100 readers to access the data simultaneously, whereas Mutex would serialise them.
Because RwLock provides a compile-time guarantee that the writer thread will always have highest priority.
Q16
Quiz
Select 0/1

Which of the following describes 'Priority Inversion'?

An optimisation where the OS scheduler prioritises background threads to save battery life.
A scenario where the main thread is forced to wait for all spawned threads to finish before exiting.
A situation where a high-priority thread is blocked by a low-priority thread holding a shared resource.
A logic error where a thread's priority is accidentally set to a negative value by the developer.
Q17
Concept Match

Match the Marker Traits

Drag each definition into its matching concept slot, then click Submit. Tap × to return a placed card to the pool.

Send
drag a definition here…
Sync
drag a definition here…
Marker Trait
drag a definition here…
Interior Mutability
drag a definition here…

Definition Pool

A type that can safely have its ownership transferred between thread boundaries.
A trait with no methods, used purely to provide information to the compiler.
A type that can safely be shared via references (&T) across multiple threads.
Types like Mutex or Atomic that allow mutation even when accessed via an immutable reference.
Q18
Quiz
Select 0/2

Why does the Rust compiler refuse to move an Rc<T> into a spawned thread?

Because Rc<T> is too large to fit in the new thread's stack space without causing an overflow.
Because moving Rc<T> could lead to two threads updating the same counter simultaneously, causing a data race.
Because the thread::spawn function is hard-coded to only accept String and integer types.
Because Rc<T> does not implement the Send trait due to its use of non-atomic reference counting.
Q19
Quiz
Select 0/1

Regarding the relationship between Send and Sync, which of the following is TRUE?

Any type that implements Sync must also explicitly implement the Copy trait.
If a type is Send, it is guaranteed to be Sync, as ownership transfer is more restrictive.
A type T is Sync if and only if a reference to it (&T) is Send.
The Sync trait is required for any data being sent through an mpsc channel.
Q20
Quiz
Select 0/2

Which of the following types are NOT Send or Sync and require 'unsafe' code or a Mutex to be used in threads? Select all that apply.

AtomicU32, as it provides hardware-level synchronisation for its internal value.
RefCell<T>, as its runtime borrow checks are not thread-safe.
Raw pointers like *mut u8, as they carry no ownership or safety information.
String, as it is a complex heap-allocated type that requires strict management.

Congratulations on completing the Chapter 10 Knowledge Test! Review any questions you missed to ensure a solid grasp of Rust’s ‘fearless concurrency’ model before moving on to networking and advanced edge patterns.