具有多线程访问的SQLitePCLRaw.provider.e_sqlite3.dll中的System.AccessViolationException或System.ExecutionEngineException崩溃

时间:2019-04-19 20:31:57

标签: c# multithreading sqlite xamarin.forms entity-framework-core

编辑:(由数字6解析)

当多个线程同时访问我的System.AccessViolationException时,导致SQLitePCLRaw.provider.e_sqlite3.dll System.ExecutionEngineExceptionFooDbContext崩溃的原因是什么?

我有一个使用NETstandard 2.0.3,Microsoft.EntityFrameworkCore 2.2.4,Microsoft.EntityFrameworkCore.Sqlite 2.2.4,支持UWP,Android和iOS的Xamarin Forms应用程序(3.5.0.169047),并且出现了可重现的崩溃这种情况(肯定会在UWP上发生),我无法解决同时从两个不同线程同时访问设备上的sqlite数据库期间出现的问题。我有一个同步过程(将本地数据推送到API并从API提取在线数据)可能需要一分钟左右的时间,因此我需要在单独的线程中执行以使UI在其操作期间保持响应。我还需要允许在同步期间查询本地数据,以允许在同步期间进行导航或在同步期间在应用程序内进行其他只读数据操作。如果我在同步过程中未执行任何数据访问操作,则长时间运行的同步效果很好,但是在完成任何中断的较短数据访问操作后立即崩溃。

从Visual Studio 2017(v15.9.5)的Debug输出中可以看到,我看到的两个崩溃异常(可能与计时相关,导致相同的复制崩溃)如下:

  1. SQLitePCLRaw.provider.e_sqlite3.dll中发生了类型为'System.AccessViolationException'的未处理异常 尝试读取或写入受保护的内存。这通常表明其他内存已损坏。

  2. SQLitePCLRaw.provider.e_sqlite3.dll中发生了'System.ExecutionEngineException'类型的未处理异常

在调试时,没有有关该异常的其他详细信息;它发生在同步代码的不同长时间运行的行中,具体取决于执行中断操作的时间;调试器显示发生异常的长时间运行代码都包含在 Try { ... } Catch(Exception) { ... }尝试阻止我的代码。

可能是什么原因导致的,我该如何解决?

我已经完成以下所有操作:

  1. SQLite用法是否与多个线程兼容?

    • 是的;根据{{​​3}},默认情况下它处于“序列化”模式,并且支持多线程使用而没有限制。正在使用的SQLite的基础版本是3.26.0,该版本是我在调试时调查Microsoft.Data.Sqlite.SQLiteConnection信息时确定的。
  2. 是因为不能同时打开多个具有写功能的连接?

    • 否;根据每个数据访问的需要,我什至使用Microsoft.Data.Sqlite.SqliteConnectionStringBuilder修改了连接字符串,使其具有适当的ModeSqliteOpenMode.ReadOnlySqliteOpenMode.ReadWriteCreate)。
  3. 是因为中断线程的FooDbContext的{​​{1}}摆脱了长时间运行的线程Dispose()所依赖的资源吗?

    • 否;我做了很多调查,因为那是崩溃之前我可能遇到的最后一个断点。即使我改写了FooDbContext中的Dispose方法却什么也不做,甚至没有调用基类的FooDbContext(不推荐,但我还是临时尝试了),崩溃仍然发生。
  4. 是否可以使用DisposeMicrosoft.Data.Sqlite.SqliteConnectionStringBuilderMicrosoft.Data.Sqlite.SQLiteConnection的{​​{1}}函数进行设置,以确保使用了序列化模式( t 100%确定是在这一点上?)

    • 否;我进行了广泛研究,该选项必须隐藏在所选SQLite库的内部。
  5. 我进行了大量的阅读和研究,但仍然没有其他可以尝试的东西。

编辑:为我解决了这个问题的答案:

  1. 我注意到异常来自的dll是SQLitePCLRaw.provider.e_sqlite3.dll。这就使我了解了实际上是如何设置低级SQLite库的,最终是对Microsoft.EntityFrameworkCore.DbContextOptionsBuilder的调用选择了要使用的特定于平台的低级SQLite提供程序。这可能是错误的吗?
    • 是的;事实证明,在通读https://xamarinhelp.com/entity-framework-core-xamarin-forms/上的Wiki信息后,对UseSqlite的调用只打算在每个平台上进行一次(使用平台特定代码或共享代码,只要{ {1}}已安装在共享项目以及每个平台特定的项目中。我的SQLitePCL.Batteries_V2.Init();用法在SQLitePCL.Batteries_V2.Init的{​​{1}}内部是错误的,这使得它在每次配置Microsoft.EntityFrameworkCore.Sqlite时调用一次,而不是在每次应用程序启动时调用一次。 SQLitePCL.Batteries.Init行从OnConfiguring中移出,并移到我共享项目中的FooDbContext构造函数中进行了修复!在中断线程的数据访问之后,崩溃不再发生。我真的希望这可以为某人节省一个巨大的麻烦,这使我避免陷入困境。

1 个答案:

答案 0 :(得分:1)

为我解决了这个问题的答案:

  1. 我注意到异常来自的dll是SQLitePCLRaw.provider.e_sqlite3.dll。这就使我了解了实际上是如何设置低级SQLite库的,最终是对SQLitePCL.Batteries_V2.Init();的调用选择了要使用的特定于平台的低级SQLite提供程序。这可能是错误的吗?
    • 是的;事实证明,在通读https://github.com/ericsink/SQLitePCL.raw/wiki/SQLitePCL.Batteries.Init#what-does-batteries_v2init-do上的Wiki信息后,对SQLitePCL.Batteries_V2.Init的调用只打算在每个平台上进行一次(使用平台特定代码或共享代码,只要{ {1}}已安装在共享项目以及每个平台特定的项目中。我的Microsoft.EntityFrameworkCore.Sqlite用法在SQLitePCL.Batteries.Init的{​​{1}}内部是错误的,这使得它在每次配置OnConfiguring时调用一次,而不是在每次应用程序启动时调用一次。 FooDbContext行从FooDbContext中移出,并移到我共享项目中的SQLitePCL.Batteries_V2.Init();构造函数中进行了修复!在中断线程的数据访问之后,崩溃不再发生。我真的希望这可以为某人节省一个巨大的麻烦,这使我避免陷入困境。