C#解决SQlite并发异常问题的方法(使用读写锁)
本文实例讲述了C#解决SQlite并发异常问题的方法。分享给大家供大家参考,具体如下:
使用C#访问sqlite时,常会遇到多线程并发导致SQLITE数据库损坏的问题。
SQLite是文件级别的数据库,其锁也是文件级别的:多个线程可以同时读,但是同时只能有一个线程写。Android提供了SqliteOpenHelper类,加入Java的锁机制以便调用。但在C#中未提供类似功能。
作者利用读写锁(ReaderWriterLock),达到了多线程安全访问的目标。
usingSystem; usingSystem.Collections.Generic; usingSystem.Text; usingSystem.Data.SQLite; usingSystem.Threading; usingSystem.Data; namespaceDataAccess { ///////////////// publicsealedclassSqliteConn { privateboolm_disposed; privatestaticDictionary<String,SQLiteConnection>connPool= newDictionary<string,SQLiteConnection>(); privatestaticDictionary<String,ReaderWriterLock>rwl= newDictionary<String,ReaderWriterLock>(); privatestaticreadonlySqliteConninstance=newSqliteConn(); privatestaticstringDEFAULT_NAME="LOCAL"; #regionInit //使用单例,解决初始化与销毁时的问题 privateSqliteConn() { rwl.Add("LOCAL",newReaderWriterLock()); rwl.Add("DB1",newReaderWriterLock()); connPool.Add("LOCAL",CreateConn("\\local.db")); connPool.Add("DB1",CreateConn("\\db1.db")); Console.WriteLine("INITFINISHED"); } privatestaticSQLiteConnectionCreateConn(stringdbName) { SQLiteConnection_conn=newSQLiteConnection(); try { stringpstr="pwd"; SQLiteConnectionStringBuilderconnstr=newSQLiteConnectionStringBuilder(); connstr.DataSource=Environment.CurrentDirectory+dbName; _conn.ConnectionString=connstr.ToString(); _conn.SetPassword(pstr); _conn.Open(); return_conn; } catch(Exceptionexp) { Console.WriteLine("===CONNCREATEERR====\r\n{0}",exp.ToString()); returnnull; } } #endregion #regionDestory //手动控制销毁,保证数据完整性 publicvoidDispose() { Dispose(true); GC.SuppressFinalize(this); } protectedvoidDispose(booldisposing) { if(!m_disposed) { if(disposing) { //Releasemanagedresources Console.WriteLine("关闭本地DB连接..."); CloseConn(); } //Releaseunmanagedresources m_disposed=true; } } ~SqliteConn() { Dispose(false); } publicvoidCloseConn() { foreach(KeyValuePair<string,SQLiteConnection>iteminconnPool) { 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(Exceptionexp) { Console.WriteLine("严重异常:无法关闭本地DB{0}的连接。",_connName); exp.ToString(); } finally { _conn=null; } } } } #endregion #regionGetConn publicstaticSqliteConnGetInstance() { returninstance; } publicSQLiteConnectionGetConnection(stringname) { SQLiteConnection_conn=connPool[name]; try { if(_conn!=null) { Console.WriteLine("TRYGETLOCK"); //加锁,直到释放前,其它线程无法得到conn rwl[name].AcquireWriterLock(3000); Console.WriteLine("LOCKGET"); return_conn; } } catch(Exceptionexp) { Console.WriteLine("===GETCONNERR====\r\n{0}",exp.StackTrace); } returnnull; } publicvoidReleaseConn(stringname) { try { //释放 Console.WriteLine("RELEASELOCK"); rwl[name].ReleaseLock(); } catch(Exceptionexp) { Console.WriteLine("===RELEASECONNERR====\r\n{0}",exp.StackTrace); } } publicSQLiteConnectionGetConnection() { returnGetConnection(DEFAULT_NAME); } publicvoidReleaseConn() { ReleaseConn(DEFAULT_NAME); } #endregion } } ////////////////////////
调用的代码如下:
SQLiteConnectionconn=null; try { conn=SqliteConn.GetInstance().GetConnection(); //在这里写自己的代码 } finally { SqliteConn.GetInstance().ReleaseConn(); }
值得注意的是,每次申请连接后,必须使用ReleaseConn方法释放,否则其它线程就再也无法得到连接了。
安全起见,在作者写的这个工具类中,启用了最严格的读写锁限制(即在写入时无法读取)。如果数据读取频繁,读者亦可开发一个得到只读连接的方法以提高性能。
在Winxp/Win7/Win8/Win8.132/64位下测试通过。
更多关于C#相关内容感兴趣的读者可查看本站专题:《C#程序设计之线程使用技巧总结》、《C#操作Excel技巧总结》、《C#中XML文件操作技巧汇总》、《C#常见控件用法教程》、《WinForm控件用法总结》、《C#数据结构与算法教程》、《C#数组操作技巧总结》及《C#面向对象程序设计入门教程》
希望本文所述对大家C#程序设计有所帮助。