全面的理解sqlite的多线程模型对于编写复杂数据存储场景的app很有必要,先来看些sqlite多线程相关的基础知识。
sqlite在多线程访问的场景下,通过db锁来控制并发,db锁有五种状态。
- Unlocked:默认状态。
- Shared:共享锁,多个读线程可以同时持有共享锁,共享锁存在时,不允许写操作发生。
- Reserved:当某个线程尝试写操作时,先持有Reserved锁,如果有多个写操作同时发生,只有一个能获得Reserved锁,当某个写线程持有Reserved锁时,其他的读线程还是可以继续加入持有Shared锁。简单来说,Reserved锁排斥其他写操作,不排斥读操作。
- Pending:某个获得Reserved锁的写操作会进一步变为Pending锁,此时新的读操作和写操作都是不能进入的,等待所有现有的写操作(Shared锁)释放之后,下一步变为Exclusive。
- Exclusive:写操作从Pending变为Exclusive,此时写操作可以安全进行。
从上面的几种锁状态可以得出结论,sqlite支持多个读操作并发执行,但同时只能有一个写操作在发生。从Reserved开始一直到Exclusive,都只能有一个写操作在进行,但在Pending之前,新的读操作都是可以继续加入,这种粒度的锁对多线程读写并发场景下读操作有较好的支持,同时也通过Pending锁避免了write starvation的问题。
针对上述锁的分析,我们在建立多线程模型的时候,主要有以下几种模型:
1.读和写都在主线程。
2.读在主线程,一个子线程复杂全部的写。
3.读在主线程,多个子线程负责并发的写。
第一种是最简陋的做法,写操作会影响UI线程的性能。第二种是比较普遍的做法,写操作都放到子线程当中,当然子线程也可以产生读操作,这种做法可以做到读写并发,同时又不影响UI线程。第三种做法使用多个写线程来提高写操作的效率,但从上面锁状态可以看出,从Reserved开始就已经是写操作互斥了,我个人感觉这种做法对写操作性能的提升相当有限。一般推荐第二种做法。