什么是仅实现Send和Sync之一的类型示例?

时间:2015-02-07 21:09:37

标签: rust

为了更好地理解SendSync特征,是否存在以下类型的示例:

  • 实施Send并且不实施Sync
  • 实施Sync并且不实施Send

2 个答案:

答案 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吗?

除非特别小心,否则。

prevnext字段不受并发访问保护,更糟糕的是如果处理程序被删除而另一个线程已获得对它的引用(例如,另一个处理程序尝试取消注册本身)然后这个悬挂引用将导致未定义的行为。

注意:后一个问题意味着仅仅为Option<*mut Handler<T>>切换AtomicPtr<ConditionHandler<T>>是不够的;有关详细信息,请参阅Common Pitfalls in Writing Lock-Free Algorithms

如果ConditionHandler<T>SyncTSyncSendSend,则SyncSend。{/ p} >

为了完整性,许多类型实现Option但不实现Vec(实际上大多数quote_id, created_at类型):例如MAX(created_at)DISTINCT ON (quote_id)

答案 1 :(得分:8)

CellRefCell实现Send但不是Sync,因为它们可以在线程之间安全地发送,但不能在它们之间共享。