0

我正在编写一个 Windows Phone 应用程序,它将数据存储在本地数据库中。我的应用程序中有多个线程可以访问数据库,到目前为止,我已经使用此处描述的技术和 AutoResetEvent 来确保任何时候只有一个线程可以访问数据库。

到目前为止,这已经非常可靠地工作了,但是现在我想添加一个 ScheduledTask 在后台做一些工作,所以我现在可能有多个进程竞争访问数据库。

谁能建议我如何调整 AutoResetEvent 技术以在 Windows Phone 上的多个进程中使用?

我见过使用互斥锁的方法。如果我在每次 DB 调用之前获取 Mutex,然后在之后释放它(类似于我使用 AutoResetEvent 的方式),这会成功吗?这种技术有什么潜在的问题吗?例如:性能?

4

3 回答 3

2

好的,首先我的问题实际上是两个问题:

  1. 需要确保如果前台应用程序正在运行,则后台进程不会运行
  2. 需要确保一次只有一个线程可以访问数据库,并且这需要跨进程工作,以适应(诚然很少见,但可能)在后台进程正在进行时启动前台应用程序的情况。

基于此线程中所做的出色工作,我创建了几个类来提供帮助。

为了解决问题(1),我创建了 SingleInstanceSynchroniser:

/// <summary>
/// Used to ensure only one instance (foreground app or background app) runs at once
/// </summary>
public class SingleInstanceSynchroniser : IDisposable
{
    private bool hasHandle = false;
    Mutex mutex;

    private void InitMutex()
    {
        string mutexId = "Global\\SingleInstanceSynchroniser"; 
        mutex = new Mutex(false, mutexId);
    }

    public SingleInstanceSynchroniser()
    {
        InitMutex();
        hasHandle = mutex.WaitOne(0);
    }

    public void Dispose()
    {
        if (hasHandle && mutex != null)
            mutex.ReleaseMutex();
    }

    public bool HasExclusiveHandle { get { return hasHandle; } }

}

用法:

在 App.xaml.cs 中:

...

SingleInstanceSynchroniser singleInstanceSynchroniser;

public App()
{
    singleInstanceSynchroniser = new SingleInstanceSynchroniser();

...

在 ScheduledAgent.cs 中:

SingleInstanceSynchroniser singleInstanceSynchroniser;

protected override void OnInvoke(ScheduledTask task)
    {
        singleInstanceSynchroniser = new SingleInstanceSynchroniser();

        if (singleInstanceSynchroniser.HasExclusiveHandle)
        {
            //Run background process
            ...
        }
        else
        { //Do not run if foreground app is running
            NotifyComplete();
        }
    }

为了解决问题(2),我创建了 SingleAccessSynchroniser:

/// <summary>
/// Used to ensure only one call is made to the database at once 
/// </summary>
public class SingleAccessSynchroniser : IDisposable
{
    public bool hasHandle = false;
    Mutex mutex;

    private void InitMutex()
    {
        string mutexId = "Global\\SingleAccessSynchroniser"; 
        mutex = new Mutex(false, mutexId);            
    }

    public SingleAccessSynchroniser() : this(0)
    { }

    public SingleAccessSynchroniser(int TimeOut)
    {
        InitMutex();

        if (TimeOut <= 0)
            hasHandle = mutex.WaitOne(); 
        else
            hasHandle = mutex.WaitOne(TimeOut);

        if (hasHandle == false)
            throw new TimeoutException("Timeout waiting for exclusive access on SingleInstance");
    }

    public void Release()
    {
        if (hasHandle && mutex != null)
        {
            mutex.ReleaseMutex();
            hasHandle = false;
        }
    }

    public void Dispose()
    {
        Release();
    }
}

用法:在所有数据库调用中:

using (var dbSync = new SingleAccessSynchroniser())
        {
            //Execute your database calls
        }

这已经可靠地运行了几个星期。希望别人觉得它有用。

于 2012-07-06T07:25:22.970 回答
2

我在 Windows Phone 8 上使用 Bens 解决方案时遇到了一些问题。有关这些问题的完整文档,请参阅线程。

我能够通过从“Global\SingleInstanceSynchroniser”中删除“Global\”来解决这些问题。

于 2013-01-22T07:03:27.490 回答
1

代理和应用程序之间对数据库的并发访问不应该成为问题。事实上,使用 Linq2SQL 是应用程序和代理之间通信的推荐方式之一。

在实践中,应用程序和代理很少需要同时运行,因此防止这种情况发生可能更合适。

潜在的性能问题将取决于您在做什么。您需要对此进行测量以查看它是否真的是一个问题。

于 2012-06-20T08:28:44.990 回答