Link: http://queconejo.wordpress.com/2011/09/04/windows-phone-mango-and-concurrent-database-access/
By
Most Windows Phone developers might already know that the mango release has a new API for local database access.
Most Windows Phone developers may have learned that mango has released a series of completely new APIs for accessing local databases.
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.
Generally, Windows Phone developers are very familiar with asynchronous programming. For example, when a network resource is requested, the access operation will be completed in a different county from the one that initiates the access.
What caught my attention is that the 2 don't work well together.
The work was not smooth sailing, which attracted my attention.
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 specified tions below:
If you download multiple resources from the network and save them to the local dB, it is possible that you try to import a storage resource into the local dB, however, the process of other storage resources is not over yet. In this case, the following exceptions will occur:
- Invalidoperationexception"The operation cannot be passed med because an operation on another thread has not been completed ."
- Invalidoperationexception"The operation cannot be passed med 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.
Not only store data in local databases, but an appeal exception occurs when you try to read, update, delete, or only 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 ).
Therefore, when attempting to obtain data from the local database, you should take responsibility for ensuring that these operations are not executed 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.
When using foreach to read data cyclically, be careful. Other database operations should not be performed before foreach is completed.
Ui thread
One way to unsure (suspected ensure) 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.
One way to ensure that these operations are not executed concurrently is to execute them in one thread. However, the only thread that allows us to interrupt it and execute these operations is the UI thread. However, considering the performance, we should not compete for the valuable running time of the UI thread.
Autoresetevent
A reasonably good solution can be implemented using the autoresetevent class (from the system. Threading namespace ).
Another more appropriate solution is to use autoresetevent.
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.
Autoresetevent is like a nasty thread. It ensures that only one thread is in the door at the same time. When a thread encounters an autoresetevent, it must first wait (wait ).
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:
We must ensure that all threads pass through this annoying "Door", so it is necessary to create a static variable:
//true means that the first thread that will be waiting can passpublic 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.
Before accessing datacontext, we must wait for the execution to be permitted.
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 shoshould repeat this pattern every time we need to interact with the database.
The same principle should be followed when accessing any database.
Notes
First and foremost, if a thread call“ waitone "but never call“ set" (if a thread takes its turn but doesn't give it away) no other thread will be able to get past "waitone ".
First, if the thread waitone has no other thread set, it will never be able to be executed, but it will be able to wait endlessly.
Just like with a normal revolting door, you cocould 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 ").
If multiple threads try to enter the system at the same time, long lines will be arranged outside the system.
The occasional waiting will not impact the application much, It cocould 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.
A short wait will not affect too many programs. It can easily happen when the application is started or closed, much better than unprocessed exceptions. In the background thread wait will be more desirable than the UI thread.
Relying on this mechanism to perform a large amount of database operations concurrently cocould have some additional adverse effects. e. g: other parts of the application cocould stop working if there is a large number of threads waiting in the threadpool.
Depending on this mechanism, executing a large number of database operations at the same time can have some additional adverse effects. For example, when the thread pool has too many threads to wait, other parts of the program may be suspended.
Conclusion
Using the autoresetevent we did manage to get around our problem fairly easily.
Using autoresetevent can easily solve our problem.
The first downside is that we do need to add and maintain extra code when interacting with the database.
The first is not to add and maintain additional code when accessing 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.
Second, the threads may not need to wait. Especially if you are not very concerned about the result.
Next post we'll look try to find a better solution to our problem and dive some deeper into thread synchronization.
Next time, I will try to find a better solution and thoroughly analyze the thread synchronization. (The original link has not been updated since September 2011. You can continue to look forward to it)