вопросы параллелизма - PullRequest
       12

вопросы параллелизма

5 голосов
/ 24 февраля 2010

Я работаю над приложением для iPhone, которое использует базу данных sqlite. Приложение загружает данные из Интернета в фоновом потоке с пользовательским интерфейсом в основном потоке. Поток фоновой загрузки может предварительно выполнить INSERT, UPDATE и SELECT в базе данных. Уровень пользовательского интерфейса также может взаимодействовать с базой данных, выполняя UPDATE и SELECT. Если я не сильно взаимодействую с пользовательским интерфейсом во время загрузки фонового потока, все работает нормально. Однако я начинаю сталкиваться с проблемами, когда во время загрузки выполняется много ОБНОВЛЕНИЙ в основном потоке (UI).

Приложение всегда закрывается, когда оно пытается запустить функцию базы данных. Он завершается с EXC_BAD_ACCESS, и я не вижу никаких ошибок. Например, последний раз, когда он выходил, он заканчивался на sqlite3_step:

    sqlite3_stmt *statement;
const char *query = "INSERT OR IGNORE INTO `names` (`id`,`name`) VALUES (?,?);";
if(sqlite3_prepare_v2(database, query, -1, &statement, NULL) != SQLITE_OK){
    NSAssert1(0, @"Error while creating insert statement. '%s'", sqlite3_errmsg(database));
    return NO;
}
sqlite3_bind_int(statement, 1, id);
sqlite3_bind_text(statement, 2, name, -1, SQLITE_TRANSIENT);

if(sqlite3_step(statement) != SQLITE_DONE)
    NSAssert1(0, @"Error while inserting. '%s'", sqlite3_errmsg(database));

sqlite3_finalize(statement);

Он не всегда завершается на sqlite3_step, иногда он завершается на sqlite3_prepare_v2 или sqlite3_exec. Я попытался поместить эти операторы в цикл и попробовать еще раз, если он не возвращает ОК, но это тоже не работает:

int returnCode = 0;
do{
    returnCode = sqlite3_step(statement);
    if(returnCode != SQLITE_DONE){
        usleep(20);
    }
}while(returnCode != SQLITE_DONE);

Я также пробовал транзакции SQL, но это не имеет никакого значения. Как я могу решить это? Кажется, это довольно простой вопрос параллелизма, но я не видел ничего, что могло бы помочь мне.

Спасибо за вашу помощь, Justin

Ответы [ 5 ]

2 голосов
/ 24 февраля 2010

Если вы не перекомпилируете его со специальным параметром, SQLite не является поточно-ориентированным.

См. http://www.sqlite.org/faq.html#q6

Так что вы должны позаботиться о доступе к БД и вызове SQL-операций над ней из того же потока.

Тем не менее, я нашел решение на моей стороне, которое, кажется, подходит даже в многопоточной среде: я гарантирую, что любая операция SQLite защищена директивой @synchronized, чтобы гарантировать, что когда-то поток выполняет что-то БД, любой другой поток не имеет доступа к нему.

Таким образом, вместо того, чтобы сказать «все операции SQlite должны выполняться в одном потоке», я бы скорее сказал «убедитесь, что две операции не выполняются параллельно в разных потоках».

1 голос
/ 06 августа 2011

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

dispatch_async(dispatch_get_global_queue(0, 0), ^{

    //download data from internet

    dispatch_async(dispatch_get_main_queue(), ^{
        //update database here
    }
}

Таким образом, у вас не возникнет возможных проблем с многопоточностью.Поскольку загрузка - это то, что займет больше всего времени, она выполняется в другом потоке, но обновление базы данных не должно занимать так много времени ... Так что она будет удерживать основной поток только в течение почти незаметного периода.По крайней мере, так должно быть, если запросы не медленные, а их нет.

1 голос
/ 24 февраля 2010

Я нахожусь в процессе написания программы на Objective-C, которая почти идентична поведению w.r.t.

Вот как я собираюсь синхронизировать доступ (вопрос, который я задал, не похож, но посмотрите на код):

Вызов sqlite3_close для статического дескриптора sqlite3 *

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

Я не знаю, сколько будет изменений для вашего приложения, но это может быть решением.

0 голосов
/ 12 ноября 2013

Начиная с версии 3.5.0, вы можете использовать одно и то же соединение с базой данных для нескольких потоков: http://www.sqlite.org/34to35.html Проверьте версию SQLite, которую вы используете.

Также проверьте функцию sqlite3_threadsafe.

Я написал программу на C ++, которая разделяет соединение с базой данных между двумя потоками и не получала ошибки seg (я думаю, это то же самое, что EXC_BAD_ACCESS): https://gist.github.com/allyourcode/7428159 В этом примере показано использование базы данных в памяти, но я получаю аналогичные результаты с базой данных на диске.

Я хотел бы проанализировать это с помощью инструмента гонки данных, такого как tsan, но мне нужно выяснить, как это сделать: P

0 голосов
/ 03 февраля 2011

У меня была та же проблема в моем приложении, которое работает примерно так же. Всякий раз, когда поток, обновляющий данные из Интернета, начинал записывать в базу данных в то же время, когда я выполнял какое-то взаимодействие с пользовательским интерфейсом, которое вызывало доступ к БД, программа вылетала.

@ синхронизированные операторы в каждом запросе к базе данных внутри моего обработчика БД, похоже, решают проблему.

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