Как насчет многопоточности в Android SQLite? - PullRequest
11 голосов
/ 07 апреля 2011

В моем приложении я должен реализовать некоторый интерфейс и службу синхронизации.Он работает в фоновом режиме и обновляет данные.Служба синхронизации не очень проста, она использует многопоточность.

Итак, вот моя история: когда я начинал разработку этого приложения, я ничего не знал о sqlite, поэтому я просто не использовал синхронизацию потоков вДжава.Результат: я получил много исключений, таких как «SQLiteException: база данных заблокирована: BEGIN EXCLUSIVE;»

Затем я синхронизировал все свои транзакции с обычным блоком Java synchronized () {}.Все стало намного лучше.Но я использовал Cursors для реализации CursorAdapter для моих списков.Поэтому иногда я получал одно и то же «SQLiteException: база данных заблокирована: BEGIN EXCLUSIVE;»

Я закончил создание небольшой поточно-безопасной утилиты sqlite, которая обрабатывает все эти поточно-ориентированные вещи.Кроме того, я должен использовать что-то вроде ArrayAdapter (прочитать все данные из курсора и закрыть его после чтения, а также синхронизировать этот блок) для моего пользовательского интерфейса.Итак, он работает нормально

Но мне не нравится такой способ обработки пользовательского интерфейса, потому что пользовательский интерфейс стал намного медленнее с этим решением - чтение некоторого количества данных из курсора довольно быстро, но медленнее, чем использованиеCursorAdapter

Итак, кто получил решение для этого вопроса?Спасибо

Ответы [ 5 ]

18 голосов
/ 15 апреля 2011

Итак, наконец-то вышло решение.Вот оно.

Я прочитал некоторые форумы, группы Google и обнаружил, что базу данных sqlite нужно открывать только один раз.Итак, я реализовал это с помощью singleton.

Кроме того, я реализовал некоторый код БД для синхронизации всех операций записи (чтобы предотвратить одновременное выполнение операций записи многими потоками).И мне нет дела до открытия курсоров, чтения с них.

После нескольких дней тестирования у меня нет сообщений об ошибках от моих пользователей, поэтому я думаю, что это работает

В моей предыдущей работеЯ многократно открывал базу данных sqlite в приложении, в этом была проблема.

5 голосов
/ 08 апреля 2011

SQLite реализует эксклюзивную блокировку записи, модель общей блокировки чтения. Это означает, что у вас может быть одновременно активными считыватели в базе данных или один писатель, у вас не может быть обоих. Если вы используете функцию ведения журнала WAL, у вас может быть одновременно один писатель и несколько читателей в базе данных, но вы все равно не можете иметь более одного писателя. Отличная документация по параллелизму SQLite здесь и здесь .

Возможно, вы захотите взглянуть на Беркли DB. Berkeley DB предлагает SQL API , полностью совместимый с SQLite. На самом деле мы добавили анализатор, планировщик и исполнитель SQLite поверх слоя хранения Berkeley DB. Разработчику приложений SQLite предоставляется библиотека, совместимая с SQLite, которая имеет дополнительную масштабируемость, параллелизм и надежность (HA), а также другие функции. Berkeley DB поддерживает несколько считывателей и пишет одновременно доступ к базе данных. Есть два превосходных технических документа, написанных Майком Оуэнсом, автором «Полного руководства по SQLite», в которых сравниваются Berkeley DB и SQLite ( Сравнение производительности , Поведенческие различия ).

Отказ от ответственности: я один из менеджеров по продуктам в Berkeley DB, поэтому я немного предвзят. Но такие запросы, как ваш (нужно больше параллелизма, масштабируемости, надежности от SQLite), именно поэтому мы решили предоставить комбинированную библиотеку, которая дает вам лучшее из обоих миров.

2 голосов
/ 08 апреля 2012

Если вы используете только один одноэлементный класс помощника для доступа к БД, вам не нужно синхронизировать себя, и вы можете использовать помощника от нескольких читателей / писателей, потому что класс помощника управляет самой синхронизацией.

Посмотрите на этот пост для более подробного объяснения

0 голосов
/ 12 декабря 2012

Ну, читая документацию, мой предыдущий ответ кажется неверным. У меня есть одна (тонна) SQLiteOpenHelper, поэтому кажется, что все соединения одинаковы. Даже читаемые те же, что и записываемые соединения.

Поэтому я собираюсь пропустить вызов getReadableDatabase и использовать только getWritableDatabase. Затем я собираюсь сохранить счетчик, чтобы убедиться, что база данных закрыта один раз и только один раз.

Поскольку SQLiteOpenHelper сериализует записи, все должно быть в порядке. Мне просто нужно следить за соединением, чтобы убедиться, что я не оставлю его открытым в конце.

Итак, в моем классе DbHelper у меня теперь есть:

private int activeDatabaseCount = 0;
public synchronized SQLiteDatabase openDatabase() {
    SQLiteDatabase connection = getWritableDatabase(); // always returns the same connection instance
    activeDatabaseCount++;
    return connection; 
}
public synchronized void closeDatabase(SQLiteDatabase connection) {
    activeDatabaseCount--;
    if (activeDatabaseCount == 0) {
        if (connection != null) {
            if (connection.isOpen()) {
                connection.close();
            }
        }
    }
}
0 голосов
/ 12 декабря 2012

Используйте одноэлементный помощник для открытия соединений.

1) Откройте столько читаемых соединений, сколько захотите, и закройте затем, когда вы закончите с ним.

2) Для соединений с возможностью записи вы должны открыть только одно соединение с возможностью записи.

При попытке открыть другое доступное для записи соединение верните уже открытое соединение. Если не существует доступного для записи подключения, откройте новое доступное для записи подключение. Сохраните счетчик writable_connection и закройте доступное для записи соединение, когда все потоки с ним завершат.

Я собираюсь реализовать это и сообщу всем, будет ли это работать, когда я закончу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...