我们正在使用 Xamarin 为 android 和 ios 编写带有 SQLite 的 C# 代码。然而关于如何使用sqlite,我似乎有一个概念上的误解:
根据 stackoverflow 的答案,它说 - 一个助手和一个数据库连接。使用锁来确保任何时候只有一个线程正在访问 sqlite 数据库。
我的问题是 - 如果是这样的话 - 异步有什么用?
我尝试将异步与同步代码一起使用 - 并且该代码正确地给了我编译错误以避免死锁。
为什么我不能在锁定语句体内使用“await”运算符?
public async Task InsertAsync<T> (T item){
lock (mutex) {
await asyncConnection.InsertAsync (item);
}
}
public async Task InsertOrUpdateAsync<T> (T item){
lock (mutex) {
int count = await asyncConnection.UpdateAsync (item);
if (0 == count) {
await asyncConnection.InsertAsync (item);
}
}
}
失败了。然而,如果我要使用锁来确保一次使用一个连接一个线程作为 sqlite 中的最佳实践,那么为什么会有异步 sqlite 库呢?
为什么有些线程在网络上提倡异步 sqlite 使用。
sqlite 在 android 和 iphone 中真正的最佳实践是什么?只使用同步版本?
同步与异步和单一与并发之间存在很大差异,并且它们共有 4 种组合。
在 single asynchronous 情况下,您最多使用单个线程访问数据库,但在整个操作过程中不需要是同一个线程,并且当您不需要线程时(当您等待时)为了完成
IO
操作)您根本不需要任何线程。
将
async
的使用限制为一次单个操作的最基本方法是将 SemaphoreSlim
与 initialCount = 1
一起使用。更好的方法是使用 AsyncLock
(构建异步协调原语,第 6 部分:AsyncLock,作者:Stephen Toub):
private readonly AsyncLock _lock = new AsyncLock();
public async Task InsertAsync<T> (T item)
{
using(await _lock.LockAsync())
{
await asyncConnection.InsertAsync (item);
}
}
public async Task InsertOrUpdateAsync<T> (T item)
{
using(await _lock.LockAsync())
{
if (0 == await asyncConnection.UpdateAsync (item))
{
await asyncConnection.InsertAsync (item);
}
}
}
注意:我的实现
AsyncLock
您的数据库不接受多次访问(插入、更新等)这一事实并不意味着对其进行工作的单个线程必须使用阻塞 api 来执行此操作。
如果您不必进行跨进程锁定,您可以在异步方法中使用
SemaphoreSlim.WaitAsync
代替 Mutex
来异步等待锁定:
private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(initialCount: 1);
public async Task InsertAsync<T>(T item)
{
await semaphoreSlim.WaitAsync();
try
{
await asyncConnection.InsertAsync(item);
}
finally
{
semaphoreSlim.Release();
}
}
public async Task InsertOrUpdateAsync<T>(T item)
{
await semaphoreSlim.WaitAsync();
try
{
int count = await asyncConnection.UpdateAsync(item);
if (0 == count)
{
await asyncConnection.InsertAsync(item);
}
}
finally
{
semaphoreSlim.Release();
}
}