重点讲解Send与Sync相关的并发知识

rust那些事之并发send与sync
send与sync在rust中属于marker trait,代码位于marker.rs,在标记模块中还有copy、unpin等trait。
在marker.rs中是通过auto trait来实现。
pub unsafe auto trait sync { }  
auto trait又称为opt-in, built-in trait (oibit)。这是一种不稳定的特性,每个类型都会自动实现一个特征,除非它们选择退出或包含一个不实现该特征的类型。
换言之,opt-in对应还有个opt-out,可以通过!(negative trait impl)语法来实现。
例如:下面代码中第一行表示类型wrapper实现了send,但是却没实现sync。
unsafe impl send for wrapper {}unsafe impl !sync for wrapper {}  
本节将会重点讲解send、sync相关的并发知识。
1.auto trait
可以通过安装nightly版使用feature特性。
rustup toolchain install nightly    
下面以自定义auto trait实现为例:
#![feature(negative_impls)]#![feature(auto_traits)]auto trait iscool {}impl !iscool for string {}struct mystruct;struct hasastring(string);fn check_cool(_: c) {}  
调用:
check_cool(42);check_cool(false);check_cool(mystruct);# the trait `iscool` is not implemented for `std::string`check_cool(string::new());  
这里给了一个简单的例子,展示了auto trait的用法,当没有实现(通过!)auto trait时,编译器会在编译阶段报:the trait xxx is not implemented for yyy。
2.send与sync
send含义:跨线程move,ownership。sync含义:跨线程share data,borrow。
通常在我们编译多线程代码时,会存在所有权转移、数据共享。那么问题来了,rc与原生指针是否可以在多线程使用呢?
我们打开rc(reference counting, 引用计数)的源码可以看到这里使用了negative trait,并没有实现send与sync,因此通过rc包裹的对象并不是线程安全的,只能用在单线程中。
impl !marker::send for rc {}impl !marker::sync for rc {}  
如果我们将它用在多线程中,会出什么问题呢?
fn main() {    let val = std::new(5);    let t = std::spawn(move || {        println!(this is a thread val: {},val);    });    t.join().unwrap();}  
报错:
error[e0277]: `rc` cannot be sent between threads safely...the trait `send` is not implemented for `rc`...  
与之对应,arc(atomic reference counted, 原子引用计数),可以看一下源码实现:
unsafe impl send for arc {}unsafe impl sync for arc {}  
send与sync都实现了。
send可以实现在多线程间安全传递所有权,sync可以线程安全的共享数据(例如:引用)。
此外,官方文档:当且仅当类型t的引用&t是send,t是sync。
大概意思就是如果引用都无法在多线程之前传递,那么底层数据变无法进行数据共享了。
marker.rs中还有段比较重要的代码,表示原生指针不是线程安全的,没有实现send、sync trait。
impl !send for *const t {}impl !send for *mut t {}impl !sync for *const t {}impl !sync for *mut t {}  
mutex与rwlock
mutex与rwlock相比于其他语言来说,实现了用户友好的接口,通过new即可将类型传递进去。
arc::new(foo{}))  
在go中使用mutex,张这个样子:
v   map[string]intmux sync.mutex  
可以看到rust一行便可以知道保护的是哪个数据。mutex是用来保护共享变量,所以这个变量类型t我们猜测可以是安全的,也可以是不安全的,所以sync是不被要求的,因此我们看源码:
unsafe impl send for mutex {}unsafe impl sync for mutex {}  
mutex会去实现send与sync,要求的类型t一定是具有所有权(实现send),但是并不要求数据是否是安全的(没实现sync)。
同理:rwlock是读写锁,需要满足并发读,因此要求t必须实现sync。
unsafe impl send for rwlock {}unsafe impl sync for rwlock {}  
小知识
前面讲解了raw pointer并不是线程安全的,那么如何实现线程安全呢?
其实也比较简单:可以通过如下多种方法:
自定义类型
将raw pointer包裹起来即可。
struct wrapper(*mut i32);unsafe impl send for wrapper {}unsafe impl sync for wrapper {}  
box
使用智能指针box。
box::new(my_num)


“光伏+”的应用场景同样蕴含巨大的市场
3GPP R16标准冻结和5G商用的开启将会催熟更多的新兴业务
密钥管理系统概述_密钥管理系统架构图
物联网传感器与农业融合,汇总十项科研新进展
气体检测仪有哪些使用误区,有什么注意事项
重点讲解Send与Sync相关的并发知识
未来半导体业不确定性引发的思考
多样化电气设备检测需求日益复杂
机器人企业从0到1的发展需要哪些资源?
为何5G需要用到网络切片技术
iphone8什么时候上市?iphone8最新消息:iPhone8再传噩耗简直悲剧,苹果7售价暴降三星S8售价猛
企业数字化转型趋势是“数据”引领业务变革,数据集中管控成为大势所趋
捷通华声智能外呼机器人已在金融催收、电话营销等领域掀起一股热潮
各向同性和各向异性工艺如何用于改善硅湿蚀刻
农业机器人的普遍应用是实现农业机械化的必由之路
森海塞尔发布 Momentum Sport TWS 运动耳机,支持实时心率与体温
从指静脉技术看生物识别应用的发展
封测领域无法避免价格战 厂商低价格吸引成熟IC订单
贸泽电子结盟欧姆龙共赢亚太市场
低速“广”域的物联网连接正加速到来,广和通发力NB-IoT和Cat 1 bis