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#程序设计有所帮助。