为了更好地理解Send和Sync特征,有没有这样的类型的例子:
Send
Sync
doinxwow1#
首先,重要的是要认识到大多数结构(或枚举)是Send:
Send + 'static
'a
Send + 'a
因此,您通常会期望任何Syncstruct也是Send,因为Send是如此容易达到的标准(与Sync相比,Sync需要多个线程的安全并发修改)。但是,没有什么可以阻止类型的创建者将其特别标记为not Send。例如,让我们复苏的条件!在Lisp中,条件的概念是为给定的条件设置一个处理程序(比如:FileNotFound),然后当在堆栈深处满足此条件时,将调用处理程序。如何在Rust中实现这一点?为了保持线程的独立性,您可以为条件处理程序使用线程本地存储(参见std::thread_local!)。每个条件都是一个条件处理程序的堆栈,要么只调用最上面的一个,要么从最上面的一个开始迭代,直到一个成功。但是,你会怎么设置它们呢?我会用RAII!我会将条件处理程序绑定到线程本地堆栈中,并将其注册到框架中(例如,使用插入式双向链表作为堆栈)。这样,当我完成时,条件处理程序会自动注销自己。当然,系统必须考虑到用户做了意想不到的事情(比如将条件处理程序存储在堆中,而不是按照它们创建的顺序删除它们),这就是为什么我们使用双向链表,以便处理程序可以在必要时从堆栈中间取消注册。所以我们有一个:
struct
FileNotFound
std::thread_local!
struct ConditionHandler<T> { handler: T, prev: Option<*mut ConditionHandler<T>>, next: Option<*mut ConditionHandler<T>>, }
而“真实的”处理程序由用户作为T传递。这个处理程序会是Sync吗?可能,这取决于你如何创建它,但没有理由你不能创建一个处理程序,使它的引用不能在多个线程之间共享。
T
prev
next
这个处理程序会是Send吗?除非特别小心,否则不会。prev和next字段不受并发访问的保护,更糟糕的是,如果处理程序被删除,而另一个线程已经获得了对它的引用(例如,另一个处理程序试图取消注册),那么这个现在悬挂的引用将导致Undefined Behavior。
Option<*mut Handler<T>>
AtomicPtr<ConditionHandler<T>>
你有了它:如果T是Sync,则ConditionHandler<T>是Sync,但永远不会是Send(原样)。为了完整起见,许多类型实现了Send,但没有实现Sync(实际上,大多数Send类型):例如Option或Vec。
ConditionHandler<T>
Option
Vec
czfnxgou2#
Cell和RefCell实现了Send,但没有实现Sync,因为它们可以在线程之间安全地发送,但不能在线程之间共享。
Cell
RefCell
hi3rlvi23#
对于Sync,我猜:https://doc.rust-lang.org/std/sync/struct.MutexGuard.html,它看起来不像是在上面实现了发送。对于发送只有你有细胞的家庭。
3条答案
按热度按时间doinxwow1#
首先,重要的是要认识到大多数结构(或枚举)是
Send
:Send + 'static
'a
的引用的结构都可以是Send + 'a
因此,您通常会期望任何
Sync
struct
也是Send
,因为Send
是如此容易达到的标准(与Sync
相比,Sync
需要多个线程的安全并发修改)。但是,没有什么可以阻止类型的创建者将其特别标记为not
Send
。例如,让我们复苏的条件!在Lisp中,条件的概念是为给定的条件设置一个处理程序(比如:
FileNotFound
),然后当在堆栈深处满足此条件时,将调用处理程序。如何在Rust中实现这一点?
为了保持线程的独立性,您可以为条件处理程序使用线程本地存储(参见
std::thread_local!
)。每个条件都是一个条件处理程序的堆栈,要么只调用最上面的一个,要么从最上面的一个开始迭代,直到一个成功。但是,你会怎么设置它们呢?
我会用RAII!我会将条件处理程序绑定到线程本地堆栈中,并将其注册到框架中(例如,使用插入式双向链表作为堆栈)。
这样,当我完成时,条件处理程序会自动注销自己。
当然,系统必须考虑到用户做了意想不到的事情(比如将条件处理程序存储在堆中,而不是按照它们创建的顺序删除它们),这就是为什么我们使用双向链表,以便处理程序可以在必要时从堆栈中间取消注册。
所以我们有一个:
而“真实的”处理程序由用户作为
T
传递。这个处理程序会是
Sync
吗?可能,这取决于你如何创建它,但没有理由你不能创建一个处理程序,使它的引用不能在多个线程之间共享。
prev
/next
数据成员,这些成员是私有的,不需要是Sync
。*这个处理程序会是
Send
吗?除非特别小心,否则不会。
prev
和next
字段不受并发访问的保护,更糟糕的是,如果处理程序被删除,而另一个线程已经获得了对它的引用(例如,另一个处理程序试图取消注册),那么这个现在悬挂的引用将导致Undefined Behavior。Option<*mut Handler<T>>
切换为AtomicPtr<ConditionHandler<T>>
是不够的;请参阅Common Pitfalls in Writing Lock-Free Algorithms了解更多详情。*你有了它:如果
T
是Sync
,则ConditionHandler<T>
是Sync
,但永远不会是Send
(原样)。为了完整起见,许多类型实现了
Send
,但没有实现Sync
(实际上,大多数Send
类型):例如Option
或Vec
。czfnxgou2#
Cell
和RefCell
实现了Send
,但没有实现Sync
,因为它们可以在线程之间安全地发送,但不能在线程之间共享。hi3rlvi23#
对于Sync,我猜:https://doc.rust-lang.org/std/sync/struct.MutexGuard.html,它看起来不像是在上面实现了发送。
对于发送只有你有细胞的家庭。