views:

122

answers:

3

I have a multithreaded app that uses sqlite. When two threads try to update the db at once i get the exception

Additional information: The database file is locked

I thought it would retry in a few milliseconds. My querys arent complex. The most complex one (which happens frequently) is update, select, run trivial code update/delete, commit. Why does it throw the exception? How can i make it retry a few times before throwing an exception?

+3  A: 

SQLite isn't thread safe for access, which is why you get this error message.

You should synchronize the access to the database (create an object, and "lock" it) whenever you go to update. This will cause the second thread to block and wait until the first thread's update finishes automatically.

Reed Copsey
...and perhaps you should reconsider using sqlite. Explicit locking tends either to go wrong or to kill performance through contention.
Pontus Gagge
Weird... I locked everything and i still have the error. I wrote `static object dbLock = new object();` in DB.cs (ALL my db code is there and i made sure). Then i searched for transactions and wrote `lock (dbLock) {` `}` around it and after still noticing the error i searched for all 3 .executes and made sure they were in lock blocks as well. Still i have that error. The variable is static so this is strange.
acidzombie24
I tried static volatile as well and have no luck. I wonder why this is happening.
acidzombie24
SQLite is fairly "dumb" with threading. You pretty much have to lock your read code, too (readerwriterlock will work here), since a read can block an effective write.
Reed Copsey
I am not sure if i had a read problem but i did make a stupid mistake while trying to fix the bug. I made multiple connections so while fixing this i would ALLWAYS cause the error bc i would always have another connection to the file. I set up the conditions where i initially had the exception and this solution worked :D
acidzombie24
+1  A: 

try to make your transaction / commit blocks as short as possible. The only time you can deadlock/block is with a transaction -- thus if you don't do them you won't have the problem.

That said, there are times when you need to do transactions (mostly on data updates), but don't do them while you are "run trivial code" if you can avoid it.

Hogan
hmm, i'll rewrite it. For whatever reason i thought the db would only lock while running .commit. +1
acidzombie24
A: 

A better approach may be to use an update queue, if you can do the database updates out of line with the rest of your code. For example, you could do something like:

m_updateQueue.Add(()=>InsertOrder(o));

Then you could have a dedicated update thread that processed the queue.

That code would look similar to this (I haven't compiled or tested it):

class UpdateQueue : IDisposable
{
     private object m_lockObj;
     private Queue<Action> m_queue;
     private volatile bool m_shutdown;
     private Thread m_thread;

     public UpdateQueue()
     {
         m_lockObj = new Object();
         m_queue = new Queue<Action>();
         m_thread = new Thread(ThreadLoop);
         m_thread.Start();
     }

     public void Add(Action a)
     {
         lock(m_lockObj)
         {
             m_queue.Enqueue(a);
             Monitor.Pulse(m_lockObj);
         }
     }

     public void Dispose()
     {
         if (m_thread != null)
         {
             m_shutdown = true;
             Monitor.PulseAll(m_lockObj);
             m_thread.Join();
             m_thread = null;
         }
     }

     private void ThreadLoop()
     {
         while (! m_shutdown)
         {
             Action a;
             lock (m_lockObj)
             {
                 if (m_queue.Count == 0)
                 {
                     Monitor.Wait(m_lockObj);
                 }

                 if (m_shutdown)
                 {
                     return;
                 }

                 a = m_queuue.Dequeue();
             }
             a();
         }
     }

}

Or, you could use something other than Sql Lite.

Scott Wisniewski