5

We're using the following:

  • Xamarin 3 (Xamarin Forms)
  • MonoTouch
  • sqlite.net
  • iOS simulator/hardware

The app synchronizes data with a server on a background thread. There is only one SQLite connection object shared by the entire app. Foreground queries are executed at the same time the background sync is running. All of this has worked fine on a Windows 8.1 version of the app (i.e., on MSFT Surface and similar). However once we switched to Xamarin/mono we started getting constant crashes as shown below.

Research led to this article: http://www.aaronheise.com/2012/12/monotouch-sqlite-sigsegv/

He's using using Mono.Data.SqliteClient, not sqlite.net as we are.

His solution involves explicitly disposing of Command objects in order to ensure the GC can keep up etc. When I tried to wrap my Command objects (from sqlite.net) in a using(){} clause I found out they are not disposable.

I've tried inserting 100ms delays and that stops the crashes, however it's not a viable solution for us.

Is there any hope for sqlite.net here, or should I look for a different way to use 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

2 に答える 2

4

複数のスレッドから同じ sqlite.net 接続をハミングしようとしたときに、SIGSEGV の代わりに意味のあるエラーが発生していたと確信していますが、それが原因であると思われる場合、解決策は簡単です。sqlite へのアクセスを制限する必要があります。データベースを一度に 1 つのスレッドに接続する 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. .

于 2014-06-27T14:28:15.543 に答える