面试题答案
一键面试原始代码中的happens - before关系分析
- 线程内顺序:
- 在主线程中,
let (lock, cvar) = &*pair;
先执行,然后let mut started = lock.lock().unwrap();
获取锁,接着进入while!*started
循环,在循环中调用cvar.wait(started).unwrap();
。这一系列操作在线程内存在先后顺序,前一个操作happens - before后一个操作。 - 在新创建的线程中,
let (lock, cvar) = &*pair2;
先执行,然后let mut started = lock.lock().unwrap();
获取锁,接着*started = true;
修改共享变量,最后cvar.notify_one();
唤醒等待的线程。同样在线程内存在先后顺序的happens - before关系。
- 在主线程中,
- 线程间的happens - before关系:
- 由于
notify_one()
操作在新线程中修改共享变量*started = true
之后,并且主线程中cvar.wait(started).unwrap();
等待这个通知,所以存在从新线程中*started = true
到主线程中while!*started
检查的happens - before关系。这确保了主线程在while
循环中能够看到新线程对*started
的修改。具体来说,新线程中获取锁、修改*started
、释放锁并调用notify_one
这一系列操作,happens - before 主线程中等待通知、获取锁、检查*started
的操作。
- 由于
改变代码结构的影响
- 将
notify_one
放到修改共享变量之前:- 线程内顺序依然存在,但是线程间的happens - before关系会改变。此时
notify_one
先执行,然后才修改*started = true
。主线程可能会在*started
还未被修改为true
时就收到通知被唤醒,然后检查*started
依然为false
,又进入等待状态。这样就破坏了原本确保主线程能看到新线程对*started
修改的happens - before关系,可能导致主线程一直等待,出现逻辑错误。
- 线程内顺序依然存在,但是线程间的happens - before关系会改变。此时
- 将
wait
放到获取锁之前:- 在主线程中,先调用
wait
会导致程序无法正确同步。因为wait
操作需要先获取锁(通过lock.lock().unwrap()
),然后释放锁进入等待状态,等待被唤醒后再重新获取锁。如果先调用wait
而没有获取锁,会导致程序逻辑错误,可能出现死锁或者未定义行为。这种情况下,线程间的happens - before关系也会被破坏,因为无法正确建立共享变量修改和等待唤醒之间的同步关系。
- 在主线程中,先调用