在两个线程中并发执行两个 SQLite 事务
我的 Android 应用程序中有一个特定的更新方法,它会生成大量数据 - 多达数百个数据库条目。 除了 UI 线程之外,我还有一个后台服务。两个线程都必须执行 update 方法,有时甚至同时执行 - 基本上,这是关于生成和缓存要显示的数据。 UI和后台服务都需要这些数据。
目前,我已将方法的执行包装在 ORMLite 事务中,该事务映射到普通的 SQLite 事务。然而,我担心有一天,当某些竞争条件搞乱数据缓存时,这会咬我的屁股。
问题:SQLite 事务是否可以保护我免受并发执行的影响,或者我应该实现某种在生成器方法启动时生成的工作线程,或者在生成器方法已经运行时阻塞?
更新:
我决定不依赖 SQLite 逻辑来保护我的高级 Java 方法。对我来说,解决方案如下:
- 用
synchronized
包装方法的生成部分 - 引入一个变量,用于跟踪上次执行该方法的时间(在方法末尾设置,因此它是标记执行结束)
synchronized
部分中的第一件事是检查上次执行是否在特定阈值内(例如过去 <= 100ms)- 如果是,则跳过生成
- 如果不是,则执行生成
这样就不会发生重复生成,因为当同时从两个线程访问该方法时,第一个会生成,但第二个不会。对我来说最重要的部分是它仍然处于阻塞状态,因为两个线程都依赖于调用该方法后发生的生成。
I have a certain update method in my Android App, which generates quite an amount of data - up to hundreds of database entries.
I also have a background service, aside from the UI thread. Both threads have to execute the update method, sometimes even at the same time - basically, this is about generating and caching data to display. Both the UI and background service need this data.
Currently, I have wrapped the method's execution in an ORMLite transaction, which maps to an ordinary SQLite transaction. However, I am afraid that this will bite me in the butt one day, when some race condition screws up the data cache.
The question: Do SQLite transactions protect me from concurrent execution, or should I rather implement some kind of worker thread which is spawned when the generator-method shall start, or blocking if the generator-method is already running?
UPDATE:
I have decided to not rely on SQLite logic for the protection of my high-level Java method. The solution was for me as follows:
- Wrap the generating part of the method with
synchronized
- Introduce a variable which tracks the last time of executing the method (set at the end of the method, so it is the marker of execution END)
- First thing in the
synchronized
section, check if the last execution is in a specific threshold (e.g. <= 100ms in the past)- If yes, skip generation
- If no, perform generation
In this way, duplicate generation should not take place, since when the method is accessed from two threads at the same time, the first will generate, but the second will not. The most important part for me here is that it is still blocking, since both threads rely on the generation having taken place after they have called the method.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
编辑:
我的以下陈述似乎是错误的:根据许多人的说法,SQLite 实现是线程安全的。然而,我曾经经历过线程问题,特别是在测试数据库访问时,但我认为这一定是由我的代码中的其他因素引起的。
抱歉回答有误导性。
起源:
好问题!
这里你应该非常小心
,因为标准的Android数据库访问对象(例如SQLiteDatabase
、Cursor
等)默认情况下不是线程安全的。甚至ContentProvider
似乎也无法为您提供完整的保护,除非您明确地在考虑多线程的情况下编写它们。根据 关于
ContentProvider
和线程的 Android 文档(几乎在页面末尾) :我不知道 SQLiteDatabases 是否有任何显式锁定机制(如锁定实际数据库文件)。我假设事务本身会锁定,至少是您访问数据库所用的句柄。我不知道对于数据库有多个句柄的情况,情况如何。
也许您可以尝试实现一些单例对象(也许是ContentProvider?)来访问您的数据库,但即使如此,我想您也必须管理某种“请求队列”。
您还应该考虑不从 UI 线程对文件系统(数据库位于文件系统上)进行任何调用。无法保证数据库会及时答复,并且您可能会遇到 ANR(特别是当您编写
“...这会生成相当多的数据”
时)。EDIT:
It seems I'm wrong in my below statement: The SQLite implementation is, according to many, thread safe. I have, however, bitterly experienced threading issues, especially when testing database access, but that must have been caused by other factors in my code then, I assume.
Sorry for the misleading answer.
ORIGIN:
Good question!
You should be very careful here
because the standard Android database access objects (such asSQLiteDatabase
,Cursor
etc) are not thread-safe by default. Not evenContentProvider
's seem to give you a complete protection unless you explicitly write them with multithreading in mind.According to Android documentation on
ContentProvider
's and threading (almost at the end of the page):I don't know if there is any explicit locking mechanism to SQLiteDatabases (as in locking the actual database file). I would assume that a transaction itself would lock, at least the very handle you access your database with. I don't know what is true for the case where you have multiple handles to your database.
Maybe you could try to implement some singleton object (A
ContentProvider
maybe?) to access your database with, but even then you'd have to manage some sort of "request queue" I suppose.You should also consider not to make any calls to the file system (the database is on the file system) from the UI-thread, what-so-ever. There is no guarantee that the database will answer in time and you're likely to end up with an ANR (especially as you write
"...which generates quite an amount of data"
).