本文執行個體講述了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#相關內容感興趣的讀者可查看本站專題:《C#程式設計之線程提示總結》、《C#操作Excel技巧總結》、《C#中XML檔案操作技巧匯總》、《C#常見控制項用法教程》、《WinForm控制項用法總結》、《C#資料結構與演算法教程》、《C#數組操作技巧總結》及《C#物件導向程式設計入門教程》
希望本文所述對大家C#程式設計有所協助。