由于項目是接手之前的爛尾項目博秫,經(jīng)常被吐槽說界面卡半天潦牛,后來發(fā)現(xiàn)項目里的網(wǎng)絡請求,數(shù)據(jù)庫操作都是在主線程挡育。將一些長時間的操作換到多線程或者異步之后后巴碗,用戶交互是變的順暢多了,可是經(jīng)常出現(xiàn)莫名其妙的閃退即寒,還有數(shù)據(jù)插入錯表的情況(用戶表數(shù)據(jù)插入到消息表中)橡淆。
因為項目比較早,用的三方庫都比較舊母赵,所以數(shù)據(jù)庫用的是SQLitePersistentObject,本以為是數(shù)據(jù)庫比較老舊問題逸爵,因為數(shù)據(jù)操作遍布數(shù)據(jù)庫各處,也只能先幫辦法解決問題凹嘲,以后在考慮重構(gòu)师倔、優(yōu)化了。網(wǎng)上也沒有報出說這個三方庫有什么問題周蹭。后來看到一篇博客中詳細講解了下SQLite的線程安全機制趋艘。
原文地址:http://blog.csdn.net/diyagoanyhacker/article/details/7209888
雖然有點長疲恢,寫的不優(yōu)美,但是仔細看完瓷胧,收貨還是頗豐显拳。
重要內(nèi)容摘出來
SQLite支持3種線程模式:
單線程:禁用所有的mutex鎖,并發(fā)使用時會出錯搓萧。當SQLite編譯時加了SQLITE_THREADSAFE=0參數(shù)杂数,或者在初始化SQLite前調(diào)用sqlite3_config(SQLITE_CONFIG_SINGLETHREAD)時啟用。
多線程:只要一個數(shù)據(jù)庫連接不被多個線程同時使用就是安全的矛绘。源碼中是啟用bCoreMutex耍休,禁用bFullMutex。實際上就是禁用數(shù)據(jù)庫連接和prepared statement(準備好的語句)上的鎖货矮,因此不能在多個線程中并發(fā)使用同一個數(shù)據(jù)庫連接或prepared statement羊精。當SQLite編譯時加了SQLITE_THREADSAFE=2參數(shù)時默認啟用。若SQLITE_THREADSAFE不為0囚玫,可以在初始化SQLite前喧锦,調(diào)用sqlite3_config(SQLITE_CONFIG_MULTITHREAD)啟用;或者在創(chuàng)建數(shù)據(jù)庫連接時抓督,設置SQLITE_OPEN_NOMUTEX flag燃少。
串行:啟用所有的鎖,包括bCoreMutex和bFullMutex铃在。因為數(shù)據(jù)庫連接和prepared statement都已加鎖阵具,所以多線程使用這些對象時沒法并發(fā),也就變成串行了定铜。當SQLite編譯時加了SQLITE_THREADSAFE=1參數(shù)時默認啟用阳液。若SQLITE_THREADSAFE不為0,可以在初始化SQLite前揣炕,調(diào)用sqlite3_config(SQLITE_CONFIG_SERIALIZED)啟用帘皿;或者在創(chuàng)建數(shù)據(jù)庫連接時,設置SQLITE_OPEN_FULLMUTEX flag畸陡。
而這里所說的初始化是指調(diào)用sqlite3_initialize()函數(shù)鹰溜,這個函數(shù)在調(diào)用sqlite3_open()時會自動調(diào)用,且只有第一次調(diào)用是有效的丁恭。
另一個要說明的是prepared statement曹动,它是由數(shù)據(jù)庫連接(的pager)來管理的,使用它也可看成使用這個數(shù)據(jù)庫連接涩惑。
因此在多線程模式下仁期,并發(fā)對同一個數(shù)據(jù)庫連接調(diào)用sqlite3_prepare_v2()來創(chuàng)建prepared statement,或者對同一個數(shù)據(jù)庫連接的任何prepared statement并發(fā)調(diào)用sqlite3_bind_*()和sqlite3_step()等函數(shù)都會出錯(在iOS上,該線程會出現(xiàn)EXC_BAD_ACCESS而中止)跛蛋。這種錯誤無關(guān)讀寫熬的,就是只讀也會出錯。
文檔中給出的安全使用規(guī)則是:沒有事務正在等待執(zhí)行赊级,所有prepared statement都被finalized押框。順帶一提,調(diào)用sqlite3_threadsafe()可以獲得編譯期的SQLITE_THREADSAFE參數(shù)理逊。標準發(fā)行版是1橡伞,也就是串行模式;而iOS上是2晋被,也就是多線程模式兑徘;Python的sqlite3模塊也默認使用串行模式,可以用sqlite3.threadsafety來配置羡洛。但是默認情況下挂脑,一個線程只能使用當前線程打開的數(shù)據(jù)庫連接,除非在連接時設置了check_same_thread=False參數(shù)欲侮。
總結(jié)崭闲,數(shù)據(jù)庫要多線程操作,需要每個線程與數(shù)據(jù)庫建立連接威蕉,還需要設置鎖刁俭、線程等參數(shù)。多條線程公用同一個數(shù)據(jù)庫連接就會出錯韧涨。