为了更好地理解Send
和Sync
特征,是否存在以下类型的示例:
Send
并且不实施Sync
。Sync
并且不实施Send
。答案 0 :(得分:13)
首先,重要的是要认识到大多数结构(或枚举)都是Send
:
Send + 'static
'a
的引用的结构都可以是Send + 'a
因此,您通常会认为任何Sync
struct
也是Send
,因为Send
是一个容易触及的标准(相比之下更难)成为Sync
的bar需要从多个线程进行安全的并发修改。
但是,没有什么可以阻止类型的创建者将其明确标记为Send
。例如,让我们复苏一下条件!
在Lisp中,条件的概念是你为给定条件设置一个处理程序(比如:FileNotFound
),然后当在堆栈深处满足这个条件时,就会调用你的处理程序。
你会如何在Rust中实现它?
那么,为了保持线程独立性,您可以将线程局部存储用于条件处理程序(请参阅std::thread_local!
)。每个条件都是条件处理程序的堆栈,其中只调用了最顶层的一个或者从顶部调用开始的迭代过程,但是直到达到成功为止。
但是,那你怎么设置它们呢?
就个人而言,我使用RAII!我将绑定线程局部堆栈中的条件处理程序并将其注册到框架中(例如,使用侵入式双向链接列表作为堆栈)。
这样,当我完成后,条件处理程序会自动取消注册。
当然,系统必须考虑用户做出意想不到的事情(比如将条件处理程序存储在堆中而不是按照它们的创建顺序丢弃它们),这就是为什么我们使用双向链表,所以如果需要,处理程序可以从堆栈的中间取消注册。
所以我们有一个:
struct ConditionHandler<T> {
handler: T,
prev: Option<*mut ConditionHandler<T>>,
next: Option<*mut ConditionHandler<T>>,
}
和&#34;真实&#34;处理程序由用户传递为T
。
此处理程序是Sync
吗?
可能,取决于你如何创建它,但没有理由你不能创建一个处理程序,以便不能在多个线程之间共享对它的引用。
注意:这些线程无法访问其prev
/ next
数据成员,这些成员是私有的,无需Sync
。
此处理程序是Send
吗?
除非特别小心,否则。
prev
和next
字段不受并发访问保护,更糟糕的是如果处理程序被删除而另一个线程已获得对它的引用(例如,另一个处理程序尝试取消注册本身)然后这个悬挂引用将导致未定义的行为。
注意:后一个问题意味着仅仅为Option<*mut Handler<T>>
切换AtomicPtr<ConditionHandler<T>>
是不够的;有关详细信息,请参阅Common Pitfalls in Writing Lock-Free Algorithms。
如果ConditionHandler<T>
为Sync
,T
为Sync
,Send
为Send
,则Sync
为Send
。{/ p} >
为了完整性,许多类型实现Option
但不实现Vec
(实际上大多数quote_id, created_at
类型):例如MAX(created_at)
或DISTINCT ON (quote_id)
。
答案 1 :(得分:8)