5

我们正在使用以下内容:

  • Xamarin 3(Xamarin 形式)
  • 单触
  • 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.
4

2 回答 2

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();
    }
}

PS 显然,由于您在多个线程上执行操作,因此您需要非常小心不要引入竞争条件,这就是为什么,例如,我包含了Upsert保证以原子方式执行两步“更新或插入”操作的方法.

于 2014-06-27T14:28:15.543 回答
0

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

于 2017-01-23T19:50:59.890 回答