2010-02-24 92 views
5

我正在使用一個sqlite數據庫的iPhone應用程序。該應用程序通過主線程中的UI在後臺線程中從互聯網下載數據。後臺下載線程可以對數據庫執行INSERT,UPDATE和SELECT操作。 UI層也可以通過執行UPDATE和SELECT來與數據庫進行交互。如果我在後臺線程正在下載時不與UI進行大量交互,則一切正常。但是,當下載正在進行時,主(UI)線程上執行了大量UPDATE時,我開始遇到問題。sqlite併發問題

應用程序在嘗試運行數據庫函數時總是退出。它退出EXC_BAD_ACCESS,我不會看到任何錯誤。例如,退出它最後一次在sqlite3_step結束:

sqlite3_stmt *statement; 
const char *query = "INSERT OR IGNORE INTO `names` (`id`,`name`) VALUES (?,?);"; 
if(sqlite3_prepare_v2(database, query, -1, &statement, NULL) != SQLITE_OK){ 
    NSAssert1(0, @"Error while creating insert statement. '%s'", sqlite3_errmsg(database)); 
    return NO; 
} 
sqlite3_bind_int(statement, 1, id); 
sqlite3_bind_text(statement, 2, name, -1, SQLITE_TRANSIENT); 

if(sqlite3_step(statement) != SQLITE_DONE) 
    NSAssert1(0, @"Error while inserting. '%s'", sqlite3_errmsg(database)); 

sqlite3_finalize(statement); 

它並不總是在sqlite3_step退出,有時它退出上sqlite3_prepare_v2或sqlite3_exec。我試圖把這些語句在一個循環,然後再試一次,如果它不返回OK,但是,這並不工作,要麼:

int returnCode = 0; 
do{ 
    returnCode = sqlite3_step(statement); 
    if(returnCode != SQLITE_DONE){ 
     usleep(20); 
    } 
}while(returnCode != SQLITE_DONE); 

我也試過SQL的交易,但是這並沒有任何區別。我該如何解決這個問題?這似乎是一個相當基本的併發問題,但我沒有看到任何對我有用的東西。

感謝您所有的幫助, 賈斯汀

+0

應用*退出*?爲了好奇,它只是某種特定於Apple的版本?在香草Sqlite中,如果存在爭用,它只會返回'SQLITE_LOCKED'錯誤,並且您還可以安裝繁忙的處理程序,以在這些情況下對重試做出決定。 – 2010-02-24 10:19:11

回答

1

我在寫在Objective-C的程序是幾乎相同的w.r.t行爲的過程。

這裏是我打算如何同步訪問(我問的問題有一種不相關的,但看看代碼):

Calling sqlite3_close for a static sqlite3* handle

我將使用一個靜態NSLock實例並在寫入時將其鎖定,然後在完成時將其解鎖。

我不知道你的應用程序會有多大的變化,但它可能是一個解決方案。

2

除非您使用特殊設置重新編譯它,否則SQLite不是線程安全的。

http://www.sqlite.org/faq.html#q6

因此,它是由你來取訪問數據庫,並從同一個線程調用它的SQL操作的照顧。

但是,我想出了一個解決方案,即使在多線程環境中也可以:我確保任何SQLite操作都受到@synchronized指令的保護,以確保一旦某個線程正在數據庫,任何其他線程被阻止訪問它。

因此,我並不是說「所有的SQlite操作應該在同一個線程中完成」,而是說「確保兩個操作不是在不同的線程中並行執行」。

0

我在我的應用程序中遇到了同樣的問題,它的工作方式與此類似。每當從互聯網更新數據的線程開始寫入數據庫的同時,我做了一些觸發數據庫訪問的UI交互,程序崩潰了。

在我的數據庫處理程序中的每個數據庫查詢的同步語句似乎解決了這個問題。

1

我不確定這是否是一個有效的解決方案,但我會做的是在一個單獨的線程中下載所有數據。但是當下載完成後,返回到主線程,並在主線程中插入。

dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

    //download data from internet 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     //update database here 
    } 
} 

這樣你就不會遇到任何可能的多線程問題。由於下載是花費最多的時間,它是在另一個線程中完成的,但是數據庫的更新不應該花費那麼長的時間......因此它只會在幾乎不可察覺的時間內佔據主線程。至少它應該如果查詢不慢,並且沒有噸。

0

從版本3.5.0開始,您可以在多個線程之間共享相同的數據庫連接:http://www.sqlite.org/34to35.html檢查您正在使用的SQLite版本。

另外檢查sqlite3_threadsafe函數。我寫了一個C++程序,它共享兩個線程之間的數據庫連接,並沒有得到seg錯誤(我相信這和EXC_BAD_ACCESS是一樣的):https://gist.github.com/allyourcode/7428159這個例子顯示了使用內存數據庫,但是我得到了類似的結果結果與磁盤備份的數據庫。

我想分析這個與數據爭的工具,如燦,但我需要弄清楚如何做到這一點:P