We are using Xamarin to write C# code with SQLite for android and ios. However about how to use sqlite, I seem to have a conceptual misunderstanding:
What are the best practices for SQLite on Android?
According to the stackoverflow answer it says - one helper and one db connection. Use lock around it to make sure only one thread is accessing sqlite db at any time.
My question is - if that is the case - what is the use for async?
I tried to use async with synchronized code - and the code gave me compile errors rightfully to avoid deadlocks.
Why can't I use the 'await' operator within the body of a lock statement?
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);
}
}
}
fails. However - if I am going to use locks to ensure I am using one connection one thread at a time as a best practice in sqlite - why is there an async sqlite library at all?
And why are some threads promoting async sqlite usage on the web.
What is the real best practice for sqlite in android and iphone? Just using sync version?
As the new file isn’t locked up, so the SQLite Database is locked code 5 is no longer there. Whenever we attempt to make the database handle concurrency more than the default configuration, we face an error. In this case, rewriting a more efficient version of the code statement can help solve the error and unlock the database.
If a transaction takes more time to execute, it can prevent other threads from accessing the database, thus creating a deadlock. (A scenario in which each thread is waiting for a resource while holding a resource needed by the other thread) To resolve this issue, SQLite has a lock time out.
In order to maximize concurrency, SQLite works to minimize the amount of time that EXCLUSIVE locks are held. The operating system interface layer understands and tracks all five locking states described above.
Appropriate Uses For SQLite. SQLite is not directly comparable to client/server SQL database engines such as MySQL, Oracle, PostgreSQL, or SQL Server since SQLite is trying to solve a different problem. Client/server SQL database engines strive to implement a shared repository of enterprise data.
There's a big difference between synchronous vs asynchronous and single vs concurrent and there are all 4 combinations of them.
In the single asynchronous case you access the DB using a single thread at most, but it doesn't need to be the same thread throughout the operation, and when you don't need a thread (when you are waiting for the IO
operation to complete) you don't need any threads at all.
The most basic way to limit the async
usage to a single operation at a time is by using a SemaphoreSlim
with initialCount = 1
. A nicer way would be to use an AsyncLock
(Building Async Coordination Primitives, Part 6: AsyncLock by 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);
}
}
}
Note: My implementation of AsyncLock
The fact that your Database doesn't accept multiple accesses (insertions, updates, etc..) doesn't mean that the single thread doing work against it has to do so using a blocking api.
If you don't have to do cross-process locking, you can use SemaphoreSlim.WaitAsync
instead of your Mutex
inside your async method to await the lock asynchrnously:
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();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With