C#使用读写锁解决SQLITE并发异常问题

xiaoxiao2021-03-01  12

使用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#代码创建sqlite数据库和表缺点,数据库容易坏
转载请注明原文地址: https://www.6miu.com/read-3850062.html

最新回复(0)