简体   繁体   English

sqlite.net + monotouch = SIGSEGV崩溃

[英]sqlite.net + monotouch = SIGSEGV crashes

We're using the following: 我们使用以下内容:

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

The app synchronizes data with a server on a background thread. 该应用程序与后台线程上的服务器同步数据。 There is only one SQLite connection object shared by the entire app. 整个应用程序只共享一个SQLite连接对象。 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 (ie, on MSFT Surface and similar). 所有这些在应用程序的Windows 8.1版本上都可以正常工作(即在MSFT Surface和类似版本上)。 However once we switched to Xamarin/mono we started getting constant crashes as shown below. 然而,一旦我们切换到Xamarin / mono,我们开始不断崩溃,如下所示。

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

He's using using Mono.Data.SqliteClient, not sqlite.net as we are. 他正在使用Mono.Data.SqliteClient,而不是像我们一样使用sqlite.net。

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

I've tried inserting 100ms delays and that stops the crashes, however it's not a viable solution for us. 我尝试插入100毫秒延迟并停止崩溃,但对我们来说这不是一个可行的解决方案。

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

I'm pretty sure I was getting meaningful errors instead of SIGSEGV's when I tried hammering the same sqlite.net connection from multiple threads, but if you believe that that's the culprit, the solution is simple: you need to restrict access to any sqlite.net methods which touch the database to one thread at a time. 当我尝试从多个线程敲击相同的sqlite.net连接时,我很确定我得到了有意义的错误而不是SIGSEGV,但是如果你认为那是罪魁祸首,解决方案很简单:你需要限制对任何sqlite的访问。将数据库一次触摸到一个线程的网络方法。

In the scenario where you're sharing a single SQLiteConnection instance in your app (which is a perfectly valid way of doing things), I recommend creating a simplified proxy class wrapping your sqlite.net connection, exposing only the methods that you want and protecting access to those with lock statements, ie: 在您在应用程序中共享单个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 Obviously since you're doing things on multiple threads you need to be very careful not to introduce race conditions, which is why, for example, I included the Upsert method that is guaranteed to perform the two-step "update or insert" operation atomically. PS显然,因为你在多线程上做事,你需要非常小心,不要引入竞争条件,这就是为什么,例如,我包括保证执行两步“更新或插入”操作的Upsert方法原子。

Try adding the flags: SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex 尝试添加标志: SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex to your SQLite connection constructor. SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create | SQLiteOpenFlags.FullMutex到您的SQLite连接构造函数。 Solved our problem. 解决了我们的问题。 Looks like SQLite still does some background work after transactions, using the internal mutex ensures the base consistency. 看起来SQLite在事务之后仍然会做一些后台工作,使用内部互斥锁确保基本一致性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM