Mat*_* M. 14
首先,重要的是要意识到大多数结构(或枚举)是Send:
Send + 'static'a都可以Send + 'a因此,您通常会期望任何Sync struct一个也是Send如此,因为这Send是一个容易达到的标准(与Sync需要从多个线程进行安全并发修改的更难的存在相比).
但是,没有什么可以阻止类型的创建者将其特别标记为不Send.例如,让我们恢复条件!
在Lisp中,条件的概念是你为给定的条件设置一个处理程序(比如说:) FileNotFound,然后当在堆栈的深处这个条件满足时,就会调用你的处理程序.
你会如何在Rust中实现这一点?
好吧,为了保持线程独立性,您可以使用线程本地存储来处理条件处理程序(请参阅参考资料std::thread_local!).每个条件都是条件处理程序的堆栈,其中只调用顶部的一个或者从顶部调用开始的迭代过程,但是直到一个成功.
但是,你会如何设置它们?
就个人而言,我会使用RAII!我将绑定线程局部堆栈中的条件处理程序并将其注册到框架中(例如,使用侵入式双向链接列表作为堆栈).
这样,当我完成后,条件处理程序会自动取消注册.
当然,系统必须考虑用户做出意想不到的事情(比如将条件处理程序存储在堆中而不是按照它们的创建顺序删除它们),这就是为什么我们使用双向链表,以便处理程序如有必要,可以从堆栈中间取消注册.
所以我们有一个:
struct ConditionHandler<T> {
handler: T,
prev: Option<*mut ConditionHandler<T>>,
next: Option<*mut ConditionHandler<T>>,
}
Run Code Online (Sandbox Code Playgroud)
并且"真实"处理程序由用户传递为T.
这个处理程序会是Sync吗?
可能,取决于你如何创建它,但没有理由你不能创建一个处理程序,以便不能在多个线程之间共享对它的引用.
注意:这些线程无法访问其prev/ nextdata成员,这些成员是私有的,无需访问Sync.
这个处理程序会是Send吗?
除非特别小心,否则不.
该prev和next字段与并发访问,即使在处理程序是,而另一个线程已获得它(例如,另一个处理程序试图注销本身)的引用将被丢弃糟糕的保护,那么现在这样晃来晃去引用将导致未定义行为.
注:后一个问题意味着只是切换Option<*mut Handler<T>>的AtomicPtr<ConditionHandler<T>>不充分; 有关更多详细信息,请参阅编写无锁算法的常见缺陷.
有你有它:一个ConditionHandler<T>是Sync,如果T是Sync,但绝不会是Send(这是).
为了完整性,许多类型实现Send但不实现Sync(大多数Send类型,实际上):Option或者Vec例如.