sqlite.net + monotouch = SIGSEGV崩溃

时间:2014-06-27 14:16:18

标签: c# sqlite xamarin.ios xamarin sqlite.net

我们正在使用以下内容:

  • Xamarin 3(Xamarin Forms)
  • MonoTouch的
  • sqlite.net
  • iOS模拟器/硬件

应用程序将数据与后台线程上的服务器同步。整个应用程序只共享一个SQLite连接对象。前台查询在后台同步运行的同时执行。所有这些在应用程序的Windows 8.1版本上都可以正常工作(即在MSFT Surface和类似版本上)。然而,一旦我们切换到Xamarin / mono,我们开始不断崩溃,如下所示。

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

他使用的是Mono.Data.SqliteClient,而不是像我们一样使用sqlite.net。

他的解决方案涉及显式处理Command对象以确保GC可以保持等等。当我尝试将我的Command对象(来自sqlite.net)包装在using(){}子句中时,我发现它们不是一次性的。

我尝试插入100毫秒延迟并停止崩溃,但对我们来说这不是一个可行的解决方案。

这里对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.

2 个答案:

答案 0 :(得分:4)

我非常确定当我尝试从多个线程中敲击相同的sqlite.net连接时,我得到了有意义的错误,而不是SIGSEGV,但如果你认为那是罪魁祸首,那么解决方案很简单:您需要限制访问任何一次接触数据库的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();
    }
}

P.S。显然,因为你在多线程上做事情,你需要非常小心不要引入竞争条件,这就是为什么,例如,我包括Upsert方法,保证执行两步& #34;更新或插入&#34;原子地操作。

答案 1 :(得分:0)

尝试将标记SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex添加到SQLite连接构造函数中。解决了我们的问题。看起来SQLite在事务之后仍然会做一些后台工作,使用内部互斥锁确保基本一致性。