Сбой System.AccessViolationException или System.ExecutionEngineException в SQLitePCLRaw.provider.e_sqlite3.dll с многопоточным доступом - PullRequest
3 голосов
/ 19 апреля 2019

РЕДАКТИРОВАТЬ: (решено числом 6)

Что вызывает сбой System.AccessViolationException или System.ExecutionEngineException в SQLitePCLRaw.provider.e_sqlite3.dll, когда несколько потоков одновременно получают доступ к моему FooDbContext?

У меня есть приложение Xamarin Forms (3.5.0.169047), поддерживающее UWP, Android и iOS, использующее NETstandard 2.0.3, Microsoft.EntityFrameworkCore 2.2.4, Microsoft.EntityFrameworkCore.Sqlite 2.2.4, и у меня воспроизводимая аварийная ситуация(это наверняка случается в UWP), что у меня проблемы с разрешением, возникающие при одновременном доступе к базе данных sqlite на устройстве из двух разных потоков одновременно.У меня есть процесс синхронизации (передает локальные данные в API и извлекает онлайн-данные из API), который может занять около минуты или около того, поэтому мне нужно выполнить в отдельном потоке, чтобы пользовательский интерфейс реагировал во время его работы.Мне также нужно разрешить запросы локальных данных во время синхронизации, чтобы разрешить навигацию во время синхронизации или другие операции с данными только для чтения в приложении во время синхронизации.Длительная синхронизация работает нормально, если я не выполняю никаких операций доступа к данным во время синхронизации, но происходит сбой сразу после завершения любой прерывающей более короткой операции доступа к данным.

Два исключения сбоя, которые я виделПроисходят (возможно, связанные с синхронизацией, для которых происходит сбой в идентичных репродукциях), как видно из вывода отладки из Visual Studio 2017 (v15.9.5):

  1. Необработанное исключениеТип «System.AccessViolationException» произошел в SQLitePCLRaw.provider.e_sqlite3.dll Попытка чтения или записи в защищенную память.Это часто указывает на то, что другая память повреждена.

  2. Необработанное исключение типа 'System.ExecutionEngineException' произошло в SQLitePCLRaw.provider.e_sqlite3.dll

Во время отладки нет никаких дополнительных подробностей об исключении;это происходит во время разных длинных строк кода синхронизации, в зависимости от времени выполнения прерывающего действия;и долгосрочный код, в котором отладчик показывает возникновение исключения, содержится в блоке Try { ... } Catch(Exception) { ... } try в моем коде.

Что может быть причиной этого и как я могу его устранить?

Я прошел через все следующее:

  1. Совместимо ли использование SQLite с несколькими потоками?

    • Да;по умолчанию он находится в «Сериализованном» режиме согласно https://www.sqlite.org/threadsafe.html и поддерживает многопоточное использование без ограничений.Используемая базовая версия SQLite - 3.26.0, которую я определил при исследовании Microsoft.Data.Sqlite.SQLiteConnection информации при отладке.
  2. Это потому, что у меня не может быть большеодно соединение открыто с возможностью записи одновременно?

    • Нет;Я даже изменил свои строки подключения, используя Microsoft.Data.Sqlite.SqliteConnectionStringBuilder, чтобы получить соответствующие Mode (SqliteOpenMode.ReadOnly или SqliteOpenMode.ReadWriteCreate) в зависимости от потребностей каждого доступа к данным.
  3. Это потому, что FooDbContext прерывающего потока Dispose() избавляется от ресурсов, на которые опирался долгосрочный поток FooDbContext?

    • Нет;Я много исследовал это, так как это была последняя точка останова, которую я мог достичь до аварии.Даже когда я переопределил метод Dispose в FooDbContext, чтобы ничего не делать, даже не вызывать Dispose базового класса (не рекомендуется, но я пробовал это временно), сбой все равно происходил.
  4. Есть ли настройка, которую я могу установить с помощью функции Microsoft.Data.Sqlite.SqliteConnectionStringBuilder или Microsoft.Data.Sqlite.SQLiteConnection или Microsoft.EntityFrameworkCore.DbContextOptionsBuilder UseSqlite, чтобы убедиться, что используется сериализованный режим (я не был уверен на 100%, чтоэто точка)?

    • Нет;Я много смотрел, и эта опция должна быть скрыта во внутренностях выбранной библиотеки SQLite.
  5. Я много читал и исследовал, что все еще давало мне малоиначе я мог бы попробовать.

РЕДАКТИРОВАТЬ: Ответ, который решил эту проблему для меня:

  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, что вызов SQLitePCL.Batteries_V2.Init должен был быть выполнен только один раз для каждой платформы (либо в коде, специфичном для платформы, либо в общем коде, если * 1139) * устанавливается в общем проекте, а также в каждом конкретном проекте платформы). Мое использование SQLitePCL.Batteries.Init было неправильно внутри OnConfiguring в FooDbContext, что делало его вызываемым один раз при настройке FooDbContext, а не только один раз при запуске приложения. Перемещение строки SQLitePCL.Batteries_V2.Init(); из OnConfiguring в конструктор App.xaml.cs в моем общем проекте исправило это! Сбои больше не происходили после прерывания доступа к потоку данных. Я действительно надеюсь, что это спасет кого-то от огромных хлопот, которые он спас, пытаясь докопаться до сути.

1 Ответ

1 голос
/ 19 апреля 2019

Ответ, который решил эту проблему для меня:

  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, что вызов SQLitePCL.Batteries_V2.Init должен был быть выполнен только один раз для каждой платформы (либо в коде, специфичном для платформы, либо в общем коде, если * 1009) * устанавливается в общем проекте, а также в каждом конкретном проекте платформы). Мое использование SQLitePCL.Batteries.Init было неправильно внутри OnConfiguring в FooDbContext, что заставляло его вызываться один раз при настройке FooDbContext, а не только один раз при запуске приложения. Перемещение строки SQLitePCL.Batteries_V2.Init(); из OnConfiguring в конструктор App.xaml.cs в моем общем проекте исправило это! Сбои больше не происходили после прерывания доступа к потоку данных. Я действительно надеюсь, что это спасет кого-то от огромных хлопот, которые он спас, пытаясь докопаться до сути.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...