2014-06-27 127 views
4

我們使用以下:sqlite.net +的MonoTouch = SIGSEGV崩潰

  • Xamarin 3(Xamarin表格)
  • MonoTouch的
  • sqlite.net
  • iPhone模擬器/硬件

該應用程序與後臺線程上的服務器同步數據。整個應用程序只共享一個SQLite連接對象。前臺查詢在後臺同步運行的同時執行。所有這些在該應用程序的Windows 8.1版本(即MSFT Surface和類似版本)上運行良好。但是,一旦我們切換到Xamarin/mono,我們開始出現如下所示的不斷崩潰。

研究導致了這篇文章:http://www.aaronheise.com/2012/12/monotouch-sqlite-sigsegv/

他用使用Mono.Data.SqliteClient,不sqlite.net,因爲我們。

他的解決方案涉及顯式處置Command對象以確保GC可以跟上等等。​​當我嘗試在using(){}子句中包裝Command對象(來自sqlite.net)時,我發現它們是不一次性。

我試過插入100ms延遲,並停止崩潰,但它不是我們可行的解決方案。

sqlite.net在這裏有任何希望,或者我應該尋找一種不同的方式來使用sqlite?

mono-rt: Stacktrace: 


mono-rt: at <unknown> <0xffffffff> 

mono-rt: at (wrapper managed-to-native) SQLite.SQLite3.Prepare2 (intptr,string,int,intptr&,intptr) <IL 0x0003c, 0xffffffff> 

... 

mono-rt: 
Native stacktrace: 


mono-rt: 
Got a SIGSEGV while executing native code. This usually indicates 
a fatal error in the mono runtime or one of the native libraries 
used by your application. 

回答

4

我敢肯定我得到有意義的錯誤,而不是SIGSEGV當我試圖敲打從多個線程在同一sqlite.net連接,但如果你認爲這是罪魁禍首,解決方法很簡單的:你需要限制訪問任何一次觸及數據庫的sqlite.net方法。

在您的應用程序中共享單個SQLiteConnection實例(這是一種非常有效的方法)的情況下,我建議創建一個包裝sqlite.net連接的簡化代理類,只顯示方法你想和保護訪問與lock陳述,即:

public class DatabaseWrapper : IDisposable 
{ 
    // Fields. 
    private readonly SQLiteConnection Connection; 
    private readonly object Lock = new object(); 

    public DatabaseWrapper(string databasePath) 
    { 
     if (string.IsNullOrEmpty(databasePath)) throw new ArgumentException("Database path cannot be null or empty."); 

     this.Connection = new SQLiteConnection(databasePath); 
    } 

    public IEnumerable<T> Entities<T>() where T : new() 
    { 
     lock (this.Lock) 
     { 
      return this.Connection.Table<T>(); 
     } 
    } 

    public IEnumerable<T> Query<T>(string query, params object[] args) where T : new() 
    { 
     lock (this.Lock) 
     { 
      return this.Connection.Query<T>(query, args); 
     } 
    } 

    public int ExecuteNonQuery(string sql, params object[] args) 
    { 
     lock (this.Lock) 
     { 
      return this.Connection.Execute(sql, args); 
     } 
    } 

    public T ExecuteScalar<T>(string sql, params object[] args) 
    { 
     lock (this.Lock) 
     { 
      return this.Connection.ExecuteScalar<T>(sql, args); 
     } 
    } 

    public void Insert<T>(T entity) 
    { 
     lock (this.Lock) 
     { 
      this.Connection.Insert(entity); 
     } 
    } 

    public void Update<T>(T entity) 
    { 
     lock (this.Lock) 
     { 
      this.Connection.Update(entity); 
     } 
    } 

    public void Upsert<T>(T entity) 
    { 
     lock (this.Lock) 
     { 
      var rowCount = this.Connection.Update(entity); 

      if (rowCount == 0) 
      { 
       this.Connection.Insert(entity); 
      } 
     } 
    } 

    public void Delete<T>(T entity) 
    { 
     lock (this.Lock) 
     { 
      this.Connection.Delete(entity); 
     } 
    } 

    public void Dispose() 
    { 
     this.Connection.Dispose(); 
    } 
} 

PS很明顯,因爲你在多線程上做事情,所以你需要非常小心,不要引入競爭條件,這就是爲什麼,例如,我包含了保證以原子方式執行兩步「更新或插入」操作的方法Upsert

+0

是的,我有點偏向那種方式。我會盡力並儘快報告。看看對性能有什麼影響以及穩定性會很有趣。 –

+0

@SteveMacdonald,就穩定性而言,只要您沒有公開暴露底層連接或創建多個包裝器實例,這幾乎是無懈可擊的。至於性能,這些鎖本身的開銷可以忽略不計,但是當存在鎖爭用時你顯然會受到打擊。 –

+0

基里爾 - 無論發生在我的情況,不幸的是,通過包裝的序列化訪問沒有解決問題。我的包裝基本上和你所說的一樣,但我仍然得到seg故障。當我的測試工具僅在後臺寫入時,故障很少出現。當我將其更改爲背景上的讀/寫混合時,它立即出錯。請注意,只有在前臺線程上發出數據庫請求時,後臺纔可以無限期地運行而不會出現問題,只有這樣纔會出錯。 –

0

嘗試添加標誌:SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex到您的SQLite連接構造函數。解決了我們的問題。看起來SQLite在交易之後仍然會做一些後臺工作,使用內部互斥確保基本一致性。