软件编程
位置:首页>> 软件编程>> C#编程>> C#解决SQlite并发异常问题的方法(使用读写锁)

C#解决SQlite并发异常问题的方法(使用读写锁)

作者:yacki  发布时间:2023-08-03 04:06:02 

标签:C#,SQlite

本文实例讲述了C#解决SQlite并发异常问题的方法。分享给大家供大家参考,具体如下:

使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题。

SQLite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写。Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用。但在C#中未提供类似功能。

作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标。


using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SQLite;
using System.Threading;
using System.Data;
namespace DataAccess
{
/////////////////
public sealed class SqliteConn
{
 private bool m_disposed;
 private static Dictionary<String, SQLiteConnection> connPool =
   new Dictionary<string, SQLiteConnection>();
 private static Dictionary<String, ReaderWriterLock> rwl =
   new Dictionary<String, ReaderWriterLock>();
 private static readonly SqliteConn instance = new SqliteConn();
 private static string DEFAULT_NAME = "LOCAL";
 #region Init
 // 使用单例,解决初始化与销毁时的问题
 private SqliteConn()
 {
   rwl.Add("LOCAL", new ReaderWriterLock());
   rwl.Add("DB1", new ReaderWriterLock());
   connPool.Add("LOCAL", CreateConn("\\local.db"));
   connPool.Add("DB1", CreateConn("\\db1.db"));
   Console.WriteLine("INIT FINISHED");
 }
 private static SQLiteConnection CreateConn(string dbName)
 {
   SQLiteConnection _conn = new SQLiteConnection();
   try
   {
     string pstr = "pwd";
     SQLiteConnectionStringBuilder connstr = new SQLiteConnectionStringBuilder();
     connstr.DataSource = Environment.CurrentDirectory + dbName;
     _conn.ConnectionString = connstr.ToString();
     _conn.SetPassword(pstr);
     _conn.Open();
     return _conn;
   }
   catch (Exception exp)
   {
     Console.WriteLine("===CONN CREATE ERR====\r\n{0}", exp.ToString());
     return null;
   }
 }
 #endregion
 #region Destory
 // 手动控制销毁,保证数据完整性
 public void Dispose()
 {
   Dispose(true);
   GC.SuppressFinalize(this);
 }
 protected void Dispose(bool disposing)
 {
   if (!m_disposed)
   {
     if (disposing)
     {
       // Release managed resources
       Console.WriteLine("关闭本地DB连接...");
       CloseConn();
     }
     // Release unmanaged resources
     m_disposed = true;
   }
 }
 ~SqliteConn()
 {
   Dispose(false);
 }
 public void CloseConn()
 {
   foreach (KeyValuePair<string, SQLiteConnection> item in connPool)
   {
     SQLiteConnection _conn = item.Value;
     String _connName = item.Key;
     if (_conn != null && _conn.State != ConnectionState.Closed)
     {
       try
       {
         _conn.Close();
         _conn.Dispose();
         _conn = null;
         Console.WriteLine("Connection {0} Closed.", _connName);
       }
       catch (Exception exp)
       {
         Console.WriteLine("严重异常: 无法关闭本地DB {0} 的连接。", _connName);
         exp.ToString();
       }
       finally
       {
         _conn = null;
       }
     }
   }
 }
 #endregion
 #region GetConn
 public static SqliteConn GetInstance()
 {
   return instance;
 }
 public SQLiteConnection GetConnection(string name)
 {
   SQLiteConnection _conn = connPool[name];
   try
   {
     if (_conn != null)
     {
       Console.WriteLine("TRY GET LOCK");
       //加锁,直到释放前,其它线程无法得到conn
       rwl[name].AcquireWriterLock(3000);
       Console.WriteLine("LOCK GET");
       return _conn;
     }
   }
   catch (Exception exp)
   {
     Console.WriteLine("===GET CONN ERR====\r\n{0}", exp.StackTrace);
   }
   return null;
 }
 public void ReleaseConn(string name)
 {
   try
   {
     //释放
     Console.WriteLine("RELEASE LOCK");
     rwl[name].ReleaseLock();
   }
   catch (Exception exp)
   {
     Console.WriteLine("===RELEASE CONN ERR====\r\n{0}", exp.StackTrace);
   }
 }
 public SQLiteConnection GetConnection()
 {
   return GetConnection(DEFAULT_NAME);
 }
 public void ReleaseConn()
 {
   ReleaseConn(DEFAULT_NAME);
 }
 #endregion
}
}
////////////////////////

调用的代码如下:


SQLiteConnection conn = null;
try
{
 conn = SqliteConn.GetInstance().GetConnection();
 //在这里写自己的代码
}
finally
{
 SqliteConn.GetInstance().ReleaseConn();
}

值得注意的是,每次申请连接后,必须使用ReleaseConn方法释放,否则其它线程就再也无法得到连接了。

安全起见,在作者写的这个工具类中,启用了最严格的读写锁限制(即在写入时无法读取)。如果数据读取频繁,读者亦可开发一个得到只读连接的方法以提高性能。

在Winxp/Win7/Win8/Win8.1 32/64位下测试通过。

希望本文所述对大家C#程序设计有所帮助。

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com