2010-02-24 17 views
5

Estoy trabajando en una aplicación de iPhone que usa una base de datos sqlite. La aplicación descarga datos de Internet en una secuencia de fondo con la interfaz de usuario en el hilo principal. El hilo de descarga de fondo puede preformar INSERT, UPDATE y SELECT en la base de datos. La capa de IU también puede interactuar con la base de datos al hacer ACTUALIZACIONES y SELECCIONES. Si no interactúo mucho con la interfaz de usuario mientras se descarga el hilo de fondo, todo funciona bien. Sin embargo, comienzo a tener problemas cuando se realizan muchas ACTUALIZACIONES en el hilo principal (UI) mientras se realiza la descarga.problemas de concurrencia de sqlite

La aplicación siempre se cierra cuando intenta ejecutar una función de base de datos. Se cierra con EXC_BAD_ACCESS y no consigo ver ningún error. Por ejemplo, la última vez que dejó terminaba en 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); 

No siempre renunció el sqlite3_step, a veces se deja en sqlite3_prepare_v2 o sqlite3_exec. He intentado poner estas declaraciones en un bucle y vuelve a intentarlo si no vuelve bien, pero que no funciona bien:

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

También he tratado de transacciones SQL, pero eso no hace ninguna diferencia . ¿Como puedo resolver esto? Parece que es un problema de concurrencia bastante básico, pero no he visto nada que me funcione.

Gracias por toda su ayuda, Justin

+0

La aplicación se cierra * *? Para ser curioso, ¿es solo algún tipo de compilación específica de Apple? En vainilla Sqlite simplemente devuelve el error 'SQLITE_LOCKED' si hay contención, y también puede instalar un controlador ocupado para tomar decisiones sobre reintentos en estos casos. –

Respuesta

1

estoy en el proceso de escribir un programa en Objective-C que es un comportamiento casi idéntico w.r.t.

Así es como tengo la intención de sincronizar el acceso (La pregunta que hay una especie de relación, pero tener un vistazo al código):

Calling sqlite3_close for a static sqlite3* handle

Voy a utilizar una instancia NSLock estática y bloquearlo mientras escribo, y luego desbloquearlo cuando termine.

No sé cuánto de un cambio será para su aplicación, pero podría ser una solución.

2

A menos que lo recompile con una configuración especial, SQLite no es seguro para subprocesos.

Ver http://www.sqlite.org/faq.html#q6

Por lo que toca a usted para cuidar de acceso a la base de datos y la invocación de operaciones SQL en él desde el mismo hilo.

Sin embargo, se me ocurrió una solución por mi parte que parece estar bien incluso en un entorno multi threaded: me aseguro de que cualquier operación SQLite esté protegida con una directiva @synchronized para asegurar que una vez que un hilo está haciendo algo en el DB, cualquier otro hilo no puede acceder a él.

Por lo tanto, en lugar de decir "todas las operaciones de SQlite deben hacerse en el mismo subproceso", prefiero decir "asegúrese de que dos operaciones no se realizan en paralelo en diferentes subprocesos".

0

Tuve el mismo problema en mi aplicación que funciona de la misma manera. Cada vez que el hilo que actualizaba los datos de Internet comenzaba a escribir en la base de datos al mismo tiempo que hacía alguna interacción con UI que activaba el acceso al DB, el programa se bloqueaba.

@synchronized declaraciones en cada consulta de base de datos dentro de mi DB-handler parece resolver el problema.

1

No estoy seguro de si esta es una solución válida, pero lo que haría es descargar todos los datos en un hilo separado. Pero cuando termine la descarga, regrese al hilo principal y haga las inserciones en el hilo principal.

dispatch_async(dispatch_get_global_queue(0, 0), ^{ 

    //download data from internet 

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

De esta forma no se produce ningún problema de multihilo. Dado que la descarga es lo que tomará más tiempo, se realiza en otro hilo, pero la actualización de la base de datos no debería tomar tanto tiempo ... Por lo tanto, solo mantendrá el hilo principal durante un período casi imperceptible. Al menos debería hacerlo si las consultas no son lentas y no hay toneladas de ellas.

0

A partir de la versión 3.5.0, puede compartir la misma conexión de base de datos entre varios hilos: http://www.sqlite.org/34to35.html Compruebe la versión de SQLite que está utilizando.

También consulte la función sqlite3_threadsafe.

Escribí un programa C++ que comparte una conexión de base de datos entre dos hilos, y no obtuve fallas seg (creo que es lo mismo que EXC_BAD_ACCESS): https://gist.github.com/allyourcode/7428159 Ese ejemplo muestra el uso de la base de datos en memoria, pero obtengo resultados con base de datos respaldada por disco.

me gustaría analizar esta carrera con una herramienta de datos, tales como tsan, pero tengo que encontrar la manera de hacer eso: P

Cuestiones relacionadas