最近面試被問到FMDB的多線程處理問題,因為之前項目中是移植別人的代碼,沒有踩過這里的坑。
問題:
多線程同時訪問數據庫時,報數據庫鎖定的問題,錯誤信息是:
Unknown error finalizing or resetting statement (5: database is locked)
原因:
文件數據庫sqlite,同一時刻允許多個進程/線程讀,但同一時刻只允許一個線程寫。在操行寫操作時,數據庫文件被瑣定,此時任何其他讀/寫操作都被阻塞,如果阻塞超過5秒鍾(默認是5秒,能過重新編譯sqlite可以修改超時時間),就報"database is locked"錯誤。所以,在操作sqlite時,應該即時關閉連接;打開連接后,盡量減少非常費時的操作。
解決:
FMDatabase **不能多線程使用一個實例多線程訪問數據庫,不能使用同一個**FMDatabase 的實例。否則會發生異常。如果線程使用單獨的FMDatabase 實例是允許的,但是同樣有可能發生database is locked的問題。這是由於多線程對sqlite的競爭引起的.
FMDatabaseQueue 解決這個問題的思路是:創建一個隊列(串行線程隊列),然后將放入的隊列的block順序執行,這樣避免了多線程同時訪問數據庫
解決方案 共享同一個FMDatabaseQueue**實例**
注意:
原本使用FMDatabase類,需要手工調用db的open和close方法,但是用FMDatabaseQueue,不需要調用open,因為查看代碼發現,Queue已經open了。
所以,使用Queue,是不需要自己打開和關閉db的。但是如果使用了FMResultSet,rs倒是需要關閉,否則會報warning:
if ([db hasOpenResultSets]) {
NSLog(@"Warning: there is at least one open result set around after performing [FMDatabaseQueue inDatabase:]");
引申閱讀
加入GCD信號量控制
Reference: