天天看點

FMDB 在多線程中的使用

在App中保持一個<code>FMDatabaseQueue</code>的執行個體,并在所有的線程中都隻使用這一個執行個體。

<code>FMDatabaseQueue</code>雖然看似一個隊列,實際上它本身并不是,它通過内部建立一個Serial的<code>dispatch_queue_t</code>來處理通過<code>inDatabase</code>和<code>inTransaction</code>傳入的Blocks,是以當我們在主線程(或者背景)調用<code>inDatabase</code>或者<code>inTransaction</code>時,代碼實際上是同步的。<code>FMDatabaseQueue</code>這麼設計的目的是讓我們避免發生并發通路資料庫的問題,因為對資料庫的通路可能是随機的(在任何時候)、不同線程間(不同的網絡回調等)的請求。内置一個Serial隊列後,<code>FMDatabaseQueue</code>就變成線程安全了,所有的資料庫通路都是同步執行,而且這比使用<code>@synchronized</code>或<code>NSLock</code>要高效得多。

但是這麼一來就有了一個問題:如果背景在執行大量的更新,而主線程也需要通路資料庫,雖然要通路的資料量很少,但是在背景執行完之前,還是會阻塞主線程。

如果你是在背景使用的<code>inDatabase</code>來執行更新,可以考慮換成<code>inTransaction</code>,後者比前者更新起來快很多,特别是在更新量比較大的時候(比如更新1000條或10000條)。

拆解你的更新資料量,如果有300條,可以分10次、每次更新30條。當然有時不能這麼做,因為你可能通過網絡請求回來的資料,你希望一次性、完整地寫入到資料庫中,雖然有局限性,不過這确實能很好地減少每個Block占用資料庫的時間。

上面兩點可以改善問題,但是問題依然是存在的,在大多數時候,你應該把從主線程調用<code>inDatabase</code>和<code>inTransaction</code>放在異步裡:

這種方式能解決不依賴于資料庫傳回的結果的情況,如果對傳回結果有依賴,就需要考慮UI上的體驗了,如加一個<code>UIActivityIndicatorView</code>。