Windows Phone 数据库并行访问

原文链接: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.

大多数windows phone开发者可能已经了解到Mango已经发布了一系列全新的关于本地数据库访问的API。

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.

通常windows phone开发者对于异步编程非常熟悉了。 例如,当请求网络资源时,访问操作将在与发起访问不同的县城上完成。

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:

如果你从网络下载多个资源并将其存入本地DB,可能的情况是,你试图将一个存储资源进入本地DB,然而其他存储资源的过程却并没有结束。这种情况下将导致下述异常:

  • 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.

不止往本地DB存储数据,当你试图读取,更新,删除或只是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).

因此,当试图获取本地DB的数据时,你应当负起确保这些操作不会并发执行的责任来。

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.

当使用foreach循环读取数据时,应当小心了。在foreach完成之前,不应当进行其他的数据库操作。

UI Thread

One way to unsure(疑为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.

确保不会并发执行的一个方法是使这些操作都在一个线程中执行。然而,唯一允许我们中断它并执行这些操作的线程是UI线程。然而处于性能考虑,实在不应该争抢UI线程的宝贵运行时间。

AutoResetEvent

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

另一个比较恰当的解决方案就是使用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对于线程就像一扇令人讨厌的门。它可以保证同一时刻只会有一个线程同时在门里边。当线程遇到AutoResetEvent,它首先必须等待(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:

我们必须确保所有线程通过这扇令人讨厌的“门”,因此有必要建立一个静态变量:

//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.

我们应该确保在访问DataContext之前,必须等待被允许执行。

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”.

首先,如果线程WaitOne却没有其他线程Set,那就永远无法只需执行了,只能无休止等待。

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.

短暂等待不会影响程序太多,它可以很容易地发生在应用程序启动或关闭时,比未处理的异常要好的多。在后台线程等待将比UI线程,更可取。

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.

使用AutoResetEvent可以很轻易解决我们的问题。

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.

下次,我将试图寻找一更好的方案并深入剖析下线程同步。(原链接自September 2011未有更新,或许可以继续期待吧)

posted @ 2012-04-11 21:31  逝 者 如 斯  阅读(539)  评论(0编辑  收藏  举报