Windows Phone and concurrent database access

Most windows phone developers might already know that the Mango release has a new  API For local database access.

Typically, windows phone developers are also familiar with asynchronous programming.
E.g. when requesting a resource from the internet, the operation will complete on a different thread than it was started on.

What caught my attention is that the 2 don’t work well together.

Example

If you download multiple resources from the internet and store these in a local database, it might well be that one operation attempts to store its resource, while another operation is not finished storing another. This can result in one of the exceptions below:

  • InvalidOperationException “The operation cannot be performed because an operation on another thread has not been completed.”
  • InvalidOperationException “The operation cannot be performed during a call to SubmitChanges.”

These exceptions are not limited to storing data in the database, they will also occur when trying to read, update, delete or simply calling SubmitChanges.

Bottomline

When interacting with data in your local database, It will be up to you as a developer to make sure that you are none of it happens at the same time (=concurrently).

If you are using a foreach-loop when reading data from the database, there is reason for additional caution. You wont be able to do anything with the database until your foreach loop is completed.

UI Thread

One way to unsure that operations do not happen concurrently is to execute all of them on the same thread. However, the only thread that allows us to interrupt it and execute something in between other work is the UI Thread. And for performance reasons it doesn’t make sense to borrow time from the UI Thread to solve this problem.

AutoResetEvent

A reasonably good solution can be implemented using the AutoResetEvent class (from the System.Threading namespace).

The AutoResetEvent works like a revolting door for threads. It guarantees that no 2 threads can be “inside” at the same time: If a thread runs into such an AutoResetEvent, it will first have to wait for its turn and when its done it allows 1 other thread to take a turn.

We’ll have to make sure that all threads will go through the same “revolting door” and therefore it makes sense to create one as a static variable:

//true means that the first thread that will be waiting can pass
public static AutoResetEvent OperationOnDatabase 
        = new AutoResetEvent(true); 

Next we’ll need to make sure that any code that will interact with the DataContext will wait for its turn and allow another to take his turn once done.

try
{
    //wait for my turn
    OperationOnDatabase.WaitOne();

    //interact with the database
    //use 'ToArray' to make sure we are indeed done
    //with our DataContext before we continue
    return DataContext.Items.ToArray();
}
finally 
{
    //always give my turn away when done.
    OperationOnDatabase.Set();
}

We should repeat this pattern every time we need to interact with the database.

Notes

First and foremost, if a thread calls “WaitOne” but never calls “Set” (If a thread takes its turn but doesn’t give it away) no other thread will be able to get past “WaitOne”.

Just like with a normal revolting door, you could get a bit of a queue if there’s a lot of threads trying to enter at the same time (or threads take a long time “inside”: in between calling “WaitOne” and “Set”).

The occasional waiting will not impact the application much, it could easily happen during Application start or shut down and is preferable over unhandled exceptions. But it probably makes more sense to wait on a background thread, rather than using the UI thread for this.

Relying on this mechanism to perform a large amount of database operations concurrently could have some additional adverse effects. e.g: Other parts of the application could stop working if there is a large number of threads waiting in the threadpool.

Conclusion

Using the AutoResetEvent we did manage to get around our problem fairly easily.

The first downside is that we do need to add and maintain extra code when interacting with the database.

The second downside is the potential of threads unnecessarily waiting for each other. Especially if we are not even really interested in the result.

Next post we’ll look try to find a better solution to our problem and dive some deeper into thread synchronization.

About these ads
This entry was posted in Uncategorized and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s