C#解决SQlite并发异常问题的方法(使用读写锁)
程序员文章站
2023-08-29 20:59:54
本文实例讲述了c#解决sqlite并发异常问题的方法。分享给大家供大家参考,具体如下:
使用c#访问sqlite时,常会遇到多线程并发导致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#相关内容感兴趣的读者可查看本站专题:《c#程序设计之线程使用技巧总结》、《c#操作excel技巧总结》、《c#中xml文件操作技巧汇总》、《c#常见控件用法教程》、《winform控件用法总结》、《c#数据结构与算法教程》、《c#数组操作技巧总结》及《c#面向对象程序设计入门教程》
希望本文所述对大家c#程序设计有所帮助。