Ответ
SQLite является потокобезопасным, но с критически важными условиями.
По умолчанию SQLite скомпилирован в режиме SERIALIZED, который позволяет безопасно использовать одну базу данных из нескольких потоков. Однако, безопасность обеспечивается не самой базой данных, а правильным использованием соединений.
Основное правило: *Одно соединение с базой (`sqlite3`) должно использоваться только в одном потоке за раз.** Нельзя передавать соединение между потоками.
Правильный паттерн использования:
- Каждый поток, которому нужен доступ к базе, должен открывать свое собственное соединение.
- Каждое соединение работает независимо.
Пример (с использованием библиотеки GRDB.swift):
// Создаем очередь для базы данных, которая управляет соединением.
let dbQueue = try DatabaseQueue(path: "/path/to/database.sqlite")
// Запись из одного потока
DispatchQueue.global().async {
try! dbQueue.write { db in
try db.execute(sql: "INSERT INTO users (name) VALUES (?)", arguments: ["Alice"])
}
}
// Чтение из другого потока
DispatchQueue.global().async {
try! dbQueue.read { db in
let names = try String.fetchAll(db, sql: "SELECT name FROM users")
print(names)
}
}
Что может пойти не так?
- Разделение одного соединения между потоками: Приведет к неопределенному поведению и крашам.
- Блокировки: Длинные транзакции в одном соединении могут блокировать запись для других соединений.
Вывод: SQLite предоставляет механизмы для потокобезопасности, но ответственность за их корректное использование лежит на разработчике.