2012-06-18 3 views
0

Я пишу приложение Windows Phone, которое хранит данные в локальной базе данных. В моем приложении есть несколько потоков, которые обращаются к базе данных, и до этого момента я использовал описанный метод here с помощью AutoResetEvent, чтобы гарантировать, что только один поток может получить доступ к базе данных в любой момент времени.Многопроцессорный доступ к базе данных в Windows Phone

До сих пор это работало очень надежно, но теперь я хочу добавить ScheduledTask для выполнения некоторой работы в фоновом режиме, чтобы потенциально получить несколько процессов, конкурирующих сейчас за доступ к базе данных.

Может ли кто-нибудь посоветовать, как я могу адаптировать метод AutoResetEvent для использования в нескольких процессах на Windows Phone?

Я видел подходы с использованием Mutex. Если я получаю Mutex перед каждым вызовом DB, а затем выпускаю его позже (подобно тому, как я использую AutoResetEvent), это будет трюк? Существуют ли какие-либо потенциальные проблемы с этим методом? например: производительность?

ответ

2

ИТАК, прежде всего, моя проблема была на самом деле 2 проблемы:

  1. необходимо убедиться, что, если на переднем плане приложение работает, фоновый процесс не будет работать
  2. Необходимо убедиться, что только один поток может получить доступ к базе данных сразу, и это должно работать через процессы, чтобы обслуживать (по общему признанию, редкий, но возможный) сценарий, в котором запущено приложение переднего плана, когда выполняется фоновый процесс.

Основываясь на хорошей работе, выполненной в this thread, я создал пару классов, которые помогут.

Для решения задачи (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 
     } 

Это уже работает надежно в течение нескольких недель. Надеюсь, что кто-то найдет это полезным.

1

Параллельный доступ к базе данных между агентом и приложением не должен быть проблемой. Фактически, использование Linq2SQL является одним из recommended ways for communicating between the app and agent.

На практике редко бывает необходимо, чтобы приложение и агент запускались одновременно, поэтому может быть более целесообразным предотвратить это.

Потенциальные проблемы с производительностью будут зависеть от того, что вы делаете. Вам нужно будет измерить это, чтобы увидеть, действительно ли это проблема.

+0

Если два потока на переднем плане приложения делают запросы к базе данных, в то же время, то вы получите одну из этих ошибок: «Операция не может быть выполнена, так как операция в другом потоке не была завершена» InvalidOperationException InvalidOperationException «Операция не может быть выполнена во время вызова SubmitChanges.» Я не понимаю, почему это было бы иначе, если фоновая программа пытается получить доступ к db, поэтому ваш комментарий о том, что параллельный доступ не должен быть проблемой, немного сбивает с толку? Я нашел решение этой проблемы. Когда я получу запасной час, я отправлю его сюда :) –

2

У меня возникли проблемы с использованием решения Bens на Windows Phone 8. Пожалуйста, см. this нить для полной документации о проблемах.

Я смог решить проблемы, удалив «Global» из «Global \ SingleInstanceSynchroniser».

Смежные вопросы