如果数据不是被多个线程的指针指向,只是在单线程中使用,比如Rc,为什么还需要上锁才能写?什么情况会产生数据竞争呢? 同样的,&mut T生命周期内,如果有&T也是不行的,可是如果数据只存在于单线程又怎么会有数据竞争?
1
共 11 条评论, 1 页
如果数据不是被多个线程的指针指向,只是在单线程中使用,比如Rc,为什么还需要上锁才能写?什么情况会产生数据竞争呢? 同样的,&mut T生命周期内,如果有&T也是不行的,可是如果数据只存在于单线程又怎么会有数据竞争?
评论区
写评论你质疑的是“可变不共享,共享不可变”这个基础法则,Rc/Arc属于共享的范畴,所以原则上要不可变,除非unsafe。要想可变,就得靠锁获得唯一能改变它的排它权利,保证安全地修改。这个法则在单线程内也是要遵守的。
单线程内虽不存在数据竞争,但是会发生逻辑错误,两个地方都在修改,而你却不知情,比如下面这个例子,就很好:
上面的例子还算比较简单,要是程序逻辑上离得远的两个文件里,东边一个“可变借用”,西边一个“不可变借用”,它们借用的还是同一块内存,那西边的“不可变借用”就惨了,而且该问题很难“瞪眼法”发现,到时候就苦了!
所以,还是得感谢Rust的严格,“可变不共享,共享不可变”就算在单线程下也有意义。
Rc只是引用计数啦,如果lz说锁的话是RwLock, Mutex这些哦。这些锁是为了保证某种访问需求的,比如说某一段程序里我想要某个值一直不变,要不然变来变去不好对这个值做一些很基本的推断,比如我刚设置它为42,总不能下一行它就是另一个值吧!那我还设置它干嘛!如果lz有学过计算机组成原理/操作系统的话就会记得现在的计算机要同时运行很多程序一个方法就是这个进程运行一阵子,再另一个进程运行一阵子这样,所以刚才说的“上一行设为42,下一行就不是”这种情况就会很有可能的啦!那这样程序员就会直接骂娘啦!所以会有锁啊什么的。
我记得一个原因是,当一个 &'some_lifetime T 存在时,编译器可以在 some_lifetime 期间缓存解引用的结果并安全地使用这个缓存,而不用因为担心这期间引用指向的值被改变而每次都要解引用(因为它存在的时候不会有另一个 &mut T 和它共存嘛):
之前还找到一篇有关的文章,我一直存在收藏夹里没有细看,lz可以看看!
什么时候Rc只有上锁才能写了?需要加一个RefCell才能写差不多,但是RefCell不是锁,它甚至不是Sync的,Rc只能保证所有权被多方正确的共享,但是不能保证被多方正确借用。 或许你把线程安全和和借用规则混淆了,这两个不是一个东西,如果你的程序只在一个线程里运行,是不会有线程安全问题的,但是会存在违反借用规则导致的安全问题,解决线程安全问题才需要用到锁或原子操作,解决借用规则导致的问题则需要正确的处理借用关系,或者在编译器无法完成编译期借用检查的情况下使用运行期借用检查(例如上面的RefCell),而不是锁。典型的违反借用规则造成的问题,例如C++中的迭代器失效问题,这在单线程程序中也是会出现的,而在safe rust里就会通过借用检查避免这写问题。
这是为了说明一个任务在执行时随时可能交出控制权,再次返回时状态各预期不一致
--
👇
hangj: 这个例子本身是有问题的
--
👇
liusen-adalab: 在同一个线程中也可能出现数据竞争的
这个例子本身是有问题的
--
👇
liusen-adalab: 在同一个线程中也可能出现数据竞争的
在同一个线程中也可能出现数据竞争的
能否举个例子?
如果没有涉及到并发,我理解应该是不用的
数据是有变化的,所以需要锁
其实我也不太理解,可能是预期的问题,而不是数据竞争。 一个不可变引用,我们预期他就是永远不会变,如果中途被改变了,那结果就会跟预期不符。 其次,rc内不用加锁,加个refcell内部可变就行
rust playground