I am trying to wrap my head around Send + Sync
traits. I get the intuition behind Sync
- this is the traditional thread safety(like in C++
). The object does the necessary locking(interior mutability if needed), so threads can safely access it.
But the Send
part is bit unclear. I understand why things like Rc
are Send
only - the object can be given to a different thread, but non-atomic operations make it thread unsafe.
What is the intuition behind Send
? Does it mean the object can be copied/moved into another thread context, and continues to be valid after the copy/move?
Any examples scenarios for "Sync
but no Send
" would really help. Please also point to any rust libraries for this case (I found several for the opposite though)
For (2), I found some threads which use structs with pointers to data on stack/thread local storage as examples. But these are unsafe anyways(Sync or otherwise).
Rust captures this through the Send and Sync traits. A type is Send if it is safe to send it to another thread. A type is Sync if it is safe to share between threads (T is Sync if and only if &T is Send).
The raw pointer types are not Send because Rust cannot make any guarantees about the synchronization mechanisms that are in the underlying types, or when the pointers will be accessed. Rc is not Send because it uses a non-atomic reference counter which is not safe to send between threads.
dyn is a prefix of a trait object's type. The dyn keyword is used to highlight that calls to methods on the associated Trait are dynamically dispatched. To use the trait this way, it must be 'object safe'. Unlike generic parameters or impl Trait , the compiler does not know the concrete type that is being passed.
Sync
allows an object to to be used by two threads A and B at the same time. This is trivial for non-mutable objects, but mutations need to be synchronized (performed in sequence with the same order being seen by all threads). This is often done using a Mutex
or RwLock
which allows one thread to proceed while others must wait. By enforcing a shared order of changes, these types can turn a non-Sync
object into a Sync
object. Another mechanism for making objects Sync
is to use atomic types, which are essentially Sync
primitives.
Send
allows an object to be used by two threads A and B at different times. Thread A can create and use an object, then send it to thread B, so thread B can use the object while thread A cannot. The Rust ownership model can be used to enforce this non-overlapping use. Hence the ownership model is an important part of Rust's Send
thread safety, and may be the reason that Send
is less intuitive than Sync
when comparing with other languages.
Using the above definitions, it should be apparent why there are few examples of types that are Sync
but not Send
. If an object can be used safely by two threads at the same time (Sync
) then it can be used safely by two threads at different times (Send
). Hence, Sync
usually implies Send
. Any exception probably relates to Send
's transfer of ownership between threads, which affects which thread runs the Drop
handler and deallocates the value.
Most objects can be used safely by different threads if the uses can be guaranteed to be at different times. Hence, most types are Send
.
Rc
is an exception. It does not implement Send
. Rc
allows data to have multiple owners. If one owner in thread A could send the Rc
to another thread, giving ownership to thread B, there could be other owners in thread A that can still use the object. Since the reference count is modified non-atomically, the value of the count on the two threads may get out of sync and one thread may drop the pointed-at value while there are owners in the other thread.
Arc
is an Rc
that uses an atomic type for the reference count. Hence it can be used by multiple threads without the count getting out of sync. If the data that the Arc
points to is Sync
, the entire object is Sync
. If the data is not Sync
(e.g. a mutable type), it can be made Sync
using a Mutex
. Hence the proliferation of Arc<Mutex<T>>
types in multithreaded Rust code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With